Search code examples
randomgeneratorpython-unittest.mock

How to mock a random number generator object in python


I am writing a unit test for a function and in the real function I have:

rng = default_rng()
...
... # a little while later
while N<50:
    ...
    idx = rng.integers(100)

How do I mock out either the variable idx or the call to rng.integers? In other words, I'd like to make idx pull from a simple ordered list [0, 1, 2, ...].

Every time I try @mock.patch('numpy.random.default_rng', side_effects=[0, 1, 2, ...]) decorating the test function, the code 'runs' but doesn't do what I am hoping. If I replace the above to 'numpy.random.default_rng.integers I get an error that says default_rng has no attribute integers (I believe bc it is a generator object). I've tried a number of different iterations using @mock.patch.object but still to no avail.


Solution

  • There are some problems with your patching. First, you are obviously using from numpy.random import default_rng, so you have to patch the default_rng instance in your module - see where to patch.

    Second, integers is called on the instance of default_rng, not on the class, so you first have to get the instance of the mock, with is done via return_value.

    And third: it's called side_effect, not side_effects (though that may just be a typo in your question).

    So a working version could look like this (adapted a bit to actually be able to test something):

    sut.py

    from numpy.random import default_rng
    
    def get_random():
        rng = default_rng()
        idx = 0
        while idx < 50:
            idx = rng.integers(100)
        return idx
    

    test_sut.py

    @mock.patch('sut.default_rng')
    def test_get_random(mocked):
        mocked.return_value.integers.side_effect = range(60)
        assert do_something() == 50