Mpanarin

Mpanarin

Need help with testing and Mox

Hello!

So not so long ago I started learning Elixir (coming from python myself). To ease it up, I started a small project for myself(link). And I am trying to cover the module with tests.
I need an extremely easy thing - mock a function in my module in the tests. So I’ve decided to follow this article

To be more specific:

  1. I need to mock this function
  2. This function is used in function get_config_from_file and i want to test this function.
  3. I define a mock of this module with mox: link
  4. I try to use this mock in a test: link
  5. But when I try to call a function get_config_from_file from this mock, I get an error:
1) test config from file (Spacebrew.Config.Test)
 test/configuration_test.exs:8
 ** (Mox.UnexpectedCallError) no expectation defined for Spacebrew.ConfigMock.get_config_from_file/1 in process #PID<0.193.0>
 code: assert Spacebrew.ConfigMock.get_config_from_file(
 stacktrace:
   (mox) lib/mox.ex:551: Mox.__dispatch__/4
   test/configuration_test.exs:15: (test)

I understand that it says I should write an expect for the function i call. But why? I need it to be an original function from the module and only a paths function to be mocked.

I understand that I am wrong conceptually, I just don’t understand where and when :
Please help, as I am getting frustrated :cry:

Marked As Solved

peerreynders

peerreynders

Perhaps you are not familiar with Classical (Detroit) vs Mockist (London) Testing.

The classical (pre-mock) approach has been to make test doubles injectable by design, e.g.:

  # in the "API" module
  def get_config_from_file(params),
    do: get_config_from_file(params, &paths/0)

  # perhaps in an "implementation" module
  def get_config_from_file(params, paths) do
    Enum.reduce(paths.(), params, fn x, acc ->
      read_file(x)
      |> case do
           {:ok, content} ->
             Logger.info "\"#{x}\" file found, parsing"
             struct!(acc, parse_data(content))
           {:error, error} ->
             Logger.info "\"#{x}\" file reading error: #{error}"
             acc
         end
    end)
  end

Also Liked

LostKobrakai

LostKobrakai

So the issue here is that mox doesn’t use your implementation in lib/configuration.ex. All of the callbacks your behaviour has are expected to be different for your mock and must therefore either be expected to be called or at least stubbed with the functionality mox provides. The mock does not inherit functionality from anywhere it just know’s the contract it needs to apply to.

I hope you’re already aware of the reasons for using behaviours as requirement in mox (if not just ask), but behaviours are the written contract about what different implementing modules need to provide as functions so the system using those modules does work. So think less of it as “switching out a function, which is currently inconvenient”, but more like “switching out one piece of stand-alone functionality for another”. Behaviours make sense for e.g. gen_servers, where boilerplate functionality is provided by otp, but the details are supplied by the user. It also makes sense when using an adapter pattern like in ecto switching out databases or in bamboo/swoosh switching out email providers.

For your paths functionality I’m really wondering if you really need a behaviour (and therefore mock) in there or if your implementation is not flexible enough to get those paths (optionally) passed from a higher level entrypoint, so you can simply pass different values from your test.

gcauchon

gcauchon

You already have part of the setup to get it to work… In Spacebrew.Config, you define @callback but also provides the implementation which does not make sense.

  • The callbacks should be definde in an “abstract” module
  • The runtime binding (ie module, with @behaviour Spacebrew.Config) should be defined in config.exs
  • You already defined a “virtual” module in test_helper.exs

Since your mock is a virtual module implementing the behaviour, any function required in the scope of a test needs to be mocked.

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
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
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
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
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
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

We're in Beta

About us Mission Statement