I’m writing a test that generates a lot of ‘random’ cases. I’m using Enum.random/1
and Enum.take_random/2
and it occurred to me that I should see them, and use the ExUnit
seed too, so that I could reproduce the random cases as well.
Per the docs, both of the Enum
functions can be seeded by calling :rand.seed
.
But is it necessary or useful to seed myself, either in individual tests, or in test setup callbacks?
I would think the seed that mix test
displays is already being used to, effectively, call :rand.seed
. And that’s what it looks like the ExUnit
runner code is doing:
defp generate_test_seed(seed, %ExUnit.Test{module: module, name: name}) do
:rand.seed(@rand_algorithm, {:erlang.phash2(module), :erlang.phash2(name), seed})
end
And yet there’s also this related Stack Overflow question:
Interestingly, that question states:
I have a test case that needs to use random integer, so I have:
test "test with random integer" do IO.inspect :random.uniform(10) assert true end
This always prints
4
when I run it, even though I can see different seeds in console output:Randomized with seed 197796 ... Randomized with seed 124069
So their problem isn’t that the randomness isn’t reproducible, it’s that it’s not very random.
I tried running the test quoted above. I ran it via mix test some_file.exs:123
three (3) times and every time I saw the same 5
output to the console – weird!
I ran mix test
– i.e. all of the tests in the same (pre-existing) project to which I added the ‘test test’ – and saw the same output again, and then each time after several more runs.
I think maybe I was confused – coming from other programming languages – because :rand.seed
does NOT update ‘global state’, or at least not the state of the entire ‘run test’ execution. From the :rand
docs:
The functions with implicit state use the process dictionary variable
rand_seed
to remember the current state.If a process calls uniform/0,
uniform/1
oruniform_real/0
without setting a seed first,seed/1
is called automatically with the default algorithm and creates a non-constant seed.The functions with explicit state never use the process dictionary.
I’m guessing that it is necessary (and thus useful) to call :rand.seed
in individual tests, as I suspect they all (mostly) run in their own (Elixir/Erlang/OTP) process anyways.
Am I right? Wrong? What am I missing?