To test a polling function I want to mock the calling of a sub function so that the first time it is called it will fail, and the second time it is called it will succeed. Here's a very simplified version of it:
poll_function(var1): value = sub_function(var1) # First call will return None while not value: time.sleep(POLLING_INTERVAL) value = sub_function(var1) # A subsequent call will return a string, e.g "data" return value
Is this possible to do with a Mock
object from the mock
framework? I know Mock
objects have a call_count
attribute I should be able to use somehow.
Right now I've solved it by creating a custom mock object that I use to monkey patch sub_function()
, but I feel there should be a better less verbose way of doing it:
def test_poll(): class MyMock(object): def __init__(self, *args): self.call_count = 0 def sub_function(self, *args, **kwargs): if self.call_count > 1: return "data" else: self.call_count += 1 return None my_mock = MyMock() with patch('sub_function', my_mock.sub_function): ok_(poll_function())
side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.
With Mock you can mock magic methods but you have to define them. MagicMock has "default implementations of most of the magic methods.". If you don't need to test any magic methods, Mock is adequate and doesn't bring a lot of extraneous things into your tests.
What is mocking in Python? Mocking in Python means the unittest. mock library is being utilized to replace parts of the system with mock objects, allowing easier and more efficient unit testing than would otherwise be possible.
mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.
If I understand your question correctly, you do it by setting side_effect
to an iterable. For your simple case:
>>> mock_poll = Mock(side_effect=[None, 'data']) >>> mock_poll() None >>> mock_poll() 'data'
If you want to allow for an unlimited number of calls, use the itertools
cycle
and chain
functions:
>>> mock_poll = Mock(side_effect=chain(['first'], cycle(['others'])))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With