0

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.

1 Answers1

0

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
MrBean Bremen
  • 14,916
  • 3
  • 26
  • 46
  • This is really helpful and definitely I am getting a lot further. I have tried doing what you suggest but when I print idx it gives "" how come it doesn't show me a integer i.e 0, 1, 2 ... from the range in side effect? (And yes side effects was a typo) –  Feb 24 '22 at 02:24
  • I have tested the code I showed, and it works for me (both in Python 3.7 and 3.10). You should try to convert the range generator to a list explicitely - I changed the answer accordingly - though something must be different in your code, I guess. – MrBean Bremen Feb 24 '22 at 05:19
  • 1
    Okay thank you. I got it. You were right - my apologies what I did not show bc I didn't think it was important was that I am also mocking out another function call but in doing so I had the ordering wrong. Once I flipped them your solution is working perfectly. Thanks again so much for your help! –  Feb 24 '22 at 14:50
  • I thought they were behaving as keyword arguments when instead they were positional. –  Feb 24 '22 at 15:04
  • That's fixtures that behave like keyword arguments, not patch arguments, as they don't have a fix name. You could use the `mocker` fixture from `pytest-mock` instead of the decorator for convenience. – MrBean Bremen Feb 24 '22 at 15:41