Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Time travel and time freezing in Elixir

Tags:

When writing integration tests that depend on the current date/time, it is very handy to be able to freeze or travel to specific moment (like e.g. timecop for ruby)

Is there a way to achieve something similar in Elixir/Erlang?

I tried mocking Erlang built-ins :os.timestamp, :erlang.universaltime using meck library, however it fails with :cannot_mock_builtin.

In principle I could implement my own utility library than would enable easy mocking of current time and then use it everywhere instead of built-in methods; however, some libraries use built-ins, so this is not a viable option (e.g Ecto.Model.Timestamps, generating inserted_at and updated_at values)

like image 659
jesenko Avatar asked Oct 02 '15 09:10

jesenko


1 Answers

Elixir

I would suggest you implement this via dependency injection. For example (using the new time API in Erlang 18):

defmodule Thing do   def do_stuff(data), do: do_stuff(data, &:erlang.system_time/0)   def do_stuff(data, time), do: {data, time.()} end 

In your tests, you can easily replace the time code:

defmodule ThingTest do   use ExUnit.Case    test "the time" do     assert do_stuff("data", fn -> 123 end) == {"data", 123}   end end 

Erlang

Here's the corresponding way to do this in Erlang:

-module(thing). -export([do_stuff/1, do_stuff/2]).  do_stuff(Data) -> do_stuff(Data, fun erlang:system_time/0).  do_stuff(Data, Time) -> {Data, Time()}. 

And the test:

-module(thing_tests). -include_lib("eunit/include/eunit.hrl").  do_stuff_test() ->     ?assertEqual({"data", 123}, do_stuff("data", fun() -> 123 end). 
like image 51
Adam Lindberg Avatar answered Dec 08 '22 08:12

Adam Lindberg