Search code examples
python-3.xpython-unittestpython-mock

Can I use side_effect in my mocking to provide an indefinite number of values?


So I can use an iterable with side_effect in python mock to produce changing values returned by my calls to the mock:

some_mock.side_effect = [1, 2, 3]

return_value provides the same value every time

some_mock.return_value = 8

Is there a way I can use one or both of these methods so that a mock produces some scheduled values to begin and then an infinite response of one particular value when the first set is exhausted? i.e.:

[1, 2, 3, 8, 8, 8, 8, 8, 8, etc. etc etc.]


Solution

  • There is no specific build-in feature that does that, but you can achieve this by adding a side effect that does this.

    In the cases I can think of, it would be sufficient to just add some highest needed number of values instead of an infinite number, and use the side_effect version that takes a list:

    side_effect = [1, 2, 3] + [8] * 100
    my_mock.side_effect = side_effect
    

    If you really need that infinite number of responses, you can use the other version of side_effect instead that uses a function object instead of a list. You need some generator function that creates your infinite list, and iterate over that in your function, remembering the current position. Here is an example implementation for that (using a class to avoid global variables):

    from itertools import repeat
    
    class SideEffect:
        def __init__(self):
            self.it = self.generator()  # holds the current iterator
    
        @staticmethod
        def generator():
            yield from range(1, 4)  # yields 1, 2, 3
            yield from repeat(8)  # yields 8 infinitely
    
        def side_effect(self, *args, **kwargs):
            return next(self.it)
    
    ...
    my_mock.side_effect = SideEffect().side_effect
    

    This should have the wanted effect.