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

bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
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
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
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
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
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
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
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
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
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
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
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement