Reputation: 2071
I have functions that uses math/rand
to "randomly" sample from a Poisson and another from the binomial distribution. It is often used by other functions that also return random values like h(g(f()))
where f()
g()
and h()
are random functions.
I placed a rand.Seed(n)
call in main()
to pick a different seed every time the program is run and it works fine.
My question is for unittests for these PRNG functions and the functions that use them using the builtin testing
package. I would like to remove the randomness so that I can have a predictable value to compare with.
Where is the best place to place my constant value seed to get deterministic output? At the init() of the test file or inside every test function, or somewhere else?
Upvotes: 9
Views: 10631
Reputation: 5941
To stay compatible with parallel test execution, create your own rand.Rand.
My example includes Check()
from the standard package testing/quick
which is often used to execute tests on a hundred of pseudo-random arguments. (Similarly to OP's case, it's a function that makes your tests very much dependent on RNG seeding).
package main
import (
"math/rand"
"testing"
"testing/quick"
)
func TestRandomly(t *testing.T) {
r := rand.New(rand.NewSource(0))
config := &quick.Config{Rand: r}
assertion := func(num uint8) bool {
// fail test when argument is 254
return num != 254
}
if err := quick.Check(assertion, config); err != nil {
t.Error("failed checks", err)
}
}
Upvotes: 0
Reputation: 4814
As an alternative to passing in a math.Rand
you could monkey patch IF you you don't want dependency injection to be part of your package's API e.g.: https://play.golang.org/p/cIGxhO0wSbo
Upvotes: 1
Reputation: 417957
You should certainly not put it in the test init()
function. Why? Because execution order (or even if test functions are run) is non-deterministic. For details, see How to run golang tests sequentially?
What does this mean?
If you have 2 test functions (e.g. TestA()
and TestB()
) both of which test functions that call into math/rand
, you don't have guarantees if TestA()
is run first or TestB()
, or even if any of those will be called. And so random data returned by math/rand
will depend on this order.
A better option would be to put seeding into TestA()
and TestB()
, but this may also be insufficient, as tests may run parallel, so the random data returned by math/rand
may also be non-deterministic.
To really have deterministic test results, functions that need random data would need to receive a math.Rand
value and use that explicitly, and in tests you can create separate, distinct math.Rand
values that will not be used by other tests, so seeding those to constant values and using those in the tested functions, only then can you have deterministic results that will not depend on how and in which order the test functions are called.
Upvotes: 6