Search code examples
pythondjangorandomrandom-seedfactory-boy

Seeding the random generator for tests


I made it work using factory-boy's get_random_state/set_random_state, although it wasn't easy. And the biggest downside is that the values are big. So the thing that comes to mind is to write it to a file. But then if I accidentally run the tests not telling it to seed from the file, the value is lost. Now that I think about it, I can display the value too (think tee). But still I'd like to reduce it to 4-5 digits.

My idea is as follows. Normally when you run tests it somewhere says, "seed: 4215." Then to reproduce the same result I've got to do SEED=4215 ./manage.py test or something.

I did some experiments with factory-boy, but then I realized that I can't achieve this even with the random module itself. I tried different ideas. All of them failed so far. The simplest is this:

import random
import os
if os.getenv('A'):
    random.seed(os.getenv('A'))
else:
    seed = random.randint(0, 1000)
    random.seed(seed)
    print('seed: {}'.format(seed))
print(random.random())
print(random.random())
/app $ A= python a.py
seed: 62
0.9279915658776743
0.17302689004804395

/app $ A=62 python a.py
0.461603098412836
0.7402019819205794

Why do the results differ? And how to make them equal?


Solution

  • Currently your types are different:

    if os.getenv('A'):
        random.seed(os.getenv('A'))
    else:
        seed = random.randint(0, 1000)
        random.seed(seed)
        print('seed: {}'.format(seed))
    

    In the first case, you have a str and in the second an int. You can fix this by casting an int in the first case:

    random.seed(int(os.getenv("A")))

    I'm also not entirely following your need to seed random directly; I think with Factory Boy you can use factory.random.reseed_random (source).