Is it possible to mock methods of imported modules with unittest.mock in Python 3.5?
# file my_function.py import os my_function(): # do something with os, e.g call os.listdir return os.listdir(os.getcwd()) # file test_my_function.py test_my_function(): os = MagickMock() os.listdir = MagickMock(side_effect=["a", "b"]) self.assertEqual("a", my_function())
I expected that the the os.listdir method returns the specified side_effect “a” on the first call, but inside of my_function the unpatched os.listdir is called.
Advertisement
Answer
unittest.mock
have two main duties:
- Define
Mock
objects: object designed to follow your screenplay and record every access to your mocked object - patching references and recover the original state
In your example, you need both functionalities: Patching os.listdir
reference used in production code by a mock where you can have complete control of how it will respond. There are a lot of ways to use patch
, some details to take care on how use it and cavelets to know.
In your case you need to test my_function()
behaviour and you need to patch both os.listdir()
and os.getcwd()
. Moreover what you need is control the return_value
(take a look to the pointed documentation for return_value
and side_effect
differences).
I rewrote your example a little bit to make it more complete and clear:
my_function(nr=0): l = os.listdir(os.getcwd()) l.sort() return l[nr] @patch("os.getcwd") @patch("os.listdir", return_value=["a","b"]) def test_my_function(mock_ld, mock_g): self.assertEqual("a", my_function(0)) mock_ld.assert_called_with(mock_g.return_value) self.assertEqual("a", my_function()) self.assertEqual("b", my_function(1)) with self.assertRaises(IndexError): my_function(3)
I used the decorator syntax because I consider it the cleaner way to do it; moreover, to avoid the introduction of too many details I didn’t use autospecing that I consider a very best practice.
Last note: mocking is a powerful tool but use it and not abuse of it, patch just you need to patch and nothing more.