Search code examples
pythonpython-2.7mockingwith-statementpython-mock

Python mocking elasticsearch connection generator `with` statement


I would like to test a class with a function that has with statement in it:

func_to_test():
   ....
   with self.__elastic_generator.get() as es:
      print 'about to use the es connection....'

So I mocked the elstic_generator, and I mocked it get function when creating the class tested:

elastic_gen = mock.Mock()
elstic_gen.get = mock.Mock()
elastic_gen.get.side_effect = ['mocked elastic connection']
tested_class = TestedClass(elastic_gen)
tested_class.func_to_test()

But for some reason that doesn't work when using the with statement. However, if getting the connection without using with, like this:

x = self.__elastic_generator.get()

Then it works fine, and I get x = 'mocked elastic connection'.

So I guess the problem is related to some more functions calls being made when using the with and that I don't mock these functions.

Can someone please explain what happens under the hood and what else should I mock in order to be able to test it with the with statement?

Thanks.


Solution

  • The with statement is a concept known as a context manager. A context manager has an __enter__ function for when you're entering the the with and an __exit__ function for when you're exiting the with (either by raising or by execution finishing inside the block).

    The __enter__ function should return the value of whatever you're expecting to be assigned to the variable after as, which in this case would be es. So, to mock that, you don't want to mock the return value of .get(), you want to mock the return value of .get().__enter__(). That should look like this:

    elastic_gen = mock.Mock()
    elastic_gen.return_value.__enter__.return_value = 'mocked elastic connection'