adamu

adamu

Using Mox to mock DateTime used during init

I’m trying to use Mox to test a fix to a DateTime bug in my app.

The bug was that I was subtracting 1 from the current hour, which was failing at 00h.

I have a GenServer managing access tokens. It sends a message to itself every hour (using Process.send_after) to cleanup expired tokens. Part of this logic uses DateTime to calculate when the next cleanup should be scheduled. It schedules the first cleanup during init().

I want to test that the cleanup code works correctly at 00h, so my first thought was to mock DateTime and fix the date. DateTime doesn’t implement a behaviour, so I wrote my own behaviour + implementation that wraps the subset of DateTime functions I’m using.

The problem I’m having is how to mock this properly. I’m using Mox, and setting up a mock in test_helper.exs. However, because the Genserver uses the DateTime in init(), it tries to call the mock before I can define any expectations.

I tried adding Mox.stub_with at compile time in support/mocks.ex, to just forward to the real implementation for now, however then I get an error that Mox.Server is not started.

Is it possible to use Mox to mock a module when that module is used during application initialization?

Here’s the startup error using stub_with from the tests:

** (Mix) Could not start application my_app: MyApp.Application.start(:normal, []) returned an error: shutdown: failed to start child: MyApp.TokenServer
    ** (EXIT) an exception was raised:
        ** (Mox.UnexpectedCallError) no expectation defined for MyApp.DateTimeMock.utc_now/0 in process #PID<0.259.0> with args []
            (mox 0.5.2) lib/mox.ex:693: Mox.__dispatch__/4
            (my_app 0.1.0) lib/my_app/token_server.ex:64: MyApp.TokenServer.schedule_cleanup/0
            (my_app 0.1.0) lib/my_app/token_server.ex:13: MyApp.TokenServer.init/1

Here’s the error if I try to use stub_with at compile time:

== Compilation error in file test/support/mocks.ex ==
** (exit) exited in: GenServer.call(Mox.Server, {:add_expectation, #PID<0.172.0>, {MyApp.DateTimeMock, :utc_now, 0}, {0, [], &MyApp.DateTime.utc_now/0}}, 30000)
    ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
    (elixir 1.10.3) lib/gen_server.ex:1013: GenServer.call/3
    lib/mox.ex:547: Mox.add_expectation!/4
    lib/mox.ex:476: Mox.stub/3
    lib/mox.ex:526: anonymous fn/4 in Mox.stub_with/2

Finally, a bigger question: Any suggestions for alternative ways to test time bugs like this?

Most Liked

RudManusachi

RudManusachi

I like this idea by @benwilson512

Adzz

Adzz

I personally wrap DateTime in a helper, so I can make methods like Date.greater_than?

BUT regardless if you mock the actual datetime module and use an expect in the test it will work

def init() do
  ..
  MyApp.DateTimeUtils.utc_now()
 ...
end

Then set up mox as usual:

# test.exs
config(:my_app, date_time_module: DateTimeMock)

# somewhere:
Mox.defmock(DateTimeMock, for: DateTime.Behaviour)

# the behaviour
defmodule DateTime.Behaviour do
    @callback utc_now :: DateTime.t()
end

# the Utils Module
defmodule MyApp.DateTimeUtils do
  @behaviour DateTime.Behaviour

  @impl true
  def utc_now() do
     date_time().utc_now()
  end

  defp date_time(), do: Application.get_env(:my_app, :date_time_module, DateTime)
end
ityonemo

ityonemo

I think what you want to do is run Mox.stub_with immediately after Application.ensure_all_started(:mox) in test_helpers.exs. IIRC the Application tree is started as part of ExUnit.start

If this doesn’t work, as kobrakai suggests, you can do a thing where your server isn’t started in the Application tree, gated on Mix.env == :test (just remember to make sure it’s a compile time gate and not a runtime instruction); and you can manually start the server in test_helpers.

Where Next?

Popular in Questions Top

jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
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
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
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
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Other popular topics Top

Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39297 209
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New

We're in Beta

About us Mission Statement