lessless

lessless

Testing "seam" in Elixir

I wonder if it’s possible to mimic a simple “class reopening”/inhertinace-based SEAM in Elixir to alter a module’s behaviour without editing its code.

Here is an example in pseudo-Ruby



class PriceCalculator 

   def calculate_price(order)
     base_price = calculate_base_price(order)
     shipping = calculate_shipping(order)

     base_price + shipping
   end


  def calculate_shipping(order)
     ShippingServices.calculate_shipping(order)
  end
end

Now the calculate_shipping is enabling point to override the shipping costs calculation behaviour because we can override it in tests without altering the production code.

class Test

class PriceCalculatorStub < Price calculator
  def calculate_shipping(order)
     1000
  end
end


 def test_order_price_is_base_price_plus_shipping_price
     assert 1100 == PriceCalculatorStub.new.calculate_price(...)
  end
end

Example is adapted from Legacy Seam

Most Liked

sodapopcan

sodapopcan

There’s also Efx. It’s a cool library. Personally I found myself going back to Mox, though (not that that matters).

LostKobrakai

LostKobrakai

There’s two parts here. There’s introducing the interface of the seam and there’s how to switch out the implementation.

For the first one the answer is quite simple. Extract to a function, make the function inputs and outputs the interface. We commonly use Behaviours for that stuff given things usually don’t just need a single callback.

The latter is dependency injection in whatever form you want to do it. Personally I like explicitly passing in dependencies (e.g. GenServer.start_link(Dependency, …) is just that), but you can go with Application.get_env or some other global dependency resolver option. For tests specifically Mox will allow you to use a single behaviour implementing module to inject dynamic code to run.

I’d strongly suggest not getting hung up on the inheritance portion of this. It’s imo irrelevant to what that blog post tries to teach.

I’d also suggest to let loose of the “without altering the production code”. None of the examples of Martin Fowler left the production code untouched.

LostKobrakai

LostKobrakai

Imo that kind of thinking is a trap. You by definition now have at least two implementations, not one. It doesn’t matter that only one is used in production.

That’s fair. But I mentioned you don’t need to go the explicitly passing route. You can just as well start some global process somewhere, which provides the dependency to be used. Call that in your code and you don’t need callers of said code to change. Same with Application.get_env. That’s also global state you can read without needing callers to be involved. The big piece here is “global state” no matter the technical implementation. The technical path chosen by Martin Fowler was global state through a static class variable.

Where Next?

Popular in Discussions Top

WolfDan
After doing a port from a c++ library to my project in phoenix I’ve seen that I need a faster way to run this algorithm and I found this ...
New
AstonJ
Are there any Elixir or Erlang libraries that help with this? I’ve been thinking how streaming services like twitch have exploded recentl...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
Crowdhailer
I’ve been hearing much about the new formatter and it’s something I have been keen to try. I find examples buy far the most illuminating...
248 19204 150
New
marciol
Please, let me know if this kind of discussion already took place in another topic . Hi all, how do you consider if is better to build ...
New
sergio
There’s a new TIOBE index report that came out that shows Elixir is still not in the top 50 used languages. It also goes on to call Elix...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
hazardfn
I suppose this question is effectively hackney vs. ibrowse but we are at a point in our project where we have to make a choice between th...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
RudManusachi
What configs will make sense to put to runtime.exs? – A bit of how I configure apps: I have generic configs in config/config.exs, dev...
New

Other popular topics Top

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52341 488
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

We're in Beta

About us Mission Statement