Let’s consider how python standard unittest module suppose to use mocks.

Assume we want to test a method that creates and uses objects of other classes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# content of module.py

# classes that we want to mock
class ClassName1: 
  pass
  
class ClassName2:
  pass

# class that we want to test
class ProductionClass:
  def foo(self, parameter1, parameter2):
    object1 = module.ClassName1("some_initial_parameter1")
    intermediate_result = object1.run(parameter1)
    
    object2 = module.ClassName2("some_initial_parameter2")
    final_result = object2.run(intermediate_result, parameter2)
    
    return final_result
    

Here is how we can test object creation (Snippet is taken from official documentation):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from unittest.mock import patch
@patch('module.ClassName2')
@patch('module.ClassName1')
def test(MockClass1, MockClass2):
    module.ClassName1()
    module.ClassName2()
    assert MockClass1 is module.ClassName1
    assert MockClass2 is module.ClassName2
    assert MockClass1.called
    assert MockClass2.called

test()

I would say that in our case we need also to check that each object (object1 and object2) were instanciated and called with correct parameters. Then we can use assert_called_once_with like this (example from official documentation):

1
2
3
4
5
with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
    thing = ProductionClass()
    thing.method(1, 2, 3)

mock_method.assert_called_once_with(1, 2, 3)

Combining things together:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20

mocked_intermediate_result = "some-intermediate-result"
mocked_final_result = "some-final-result"

from unittest.mock import patch

@patch('module.ClassName2')
@patch('module.ClassName1')
def test_foo(MockClass1, MockClass2):
    MockClass1.return_value.run.return_value = mocked_intermediate_result
    MockClass2.return_value.run.return_value = mocked_final_result
    
    actual_result = module.ProductionClass().foo("parameter1", "parameter2")
    
    assert actual_result == mocked_final_result
    MockClass1.assert_called_once_with("some_initial_parameter1")
    MockClass1.return_value.run.assert_called_once_with("parameter1")
    MockClass2.assert_called_once_with("some_initial_parameter2")
    MockClass2.return_value.run.assert_called_once_with(mocked_intermediate_result, "parameter2")
    

Seems good but a little bit too verbose. Assertions on calls could be eliminated. I would like to do it like in Java with Mockito:

1
when(mockedObject.foo("parameter")).thenReturn("mocked-result");

That means you don’t need to verify intermediate calls. If they fail, the consequent calls fail as well.

The test would look like this:

1
2
3
4
5
6
7
8
9
@patch('module.ClassName2')
@patch('module.ClassName1')
def test_foo(MockClass1, MockClass2):
    when(MockClass1).__call__("some_initial_parameter1").then_when().run("parameter1").then_return(mocked_intermediate_result)
    when(MockClass2).__call__("some_initial_parameter2").then_when().run(mocked_intermediate_result, "parameter2").then_return(mocked_final_result)
        
    actual_result = module.ProductionClass().foo("parameter1", "parameter2")
    
    assert actual_result == mocked_final_result    

You don’t need calls assertions in the end. If for example object1.run(parameter1) returns something else, then condition of the second mock object is not met and the test fails.

Question: is there a way to achieve that?

There is a port of Mockito to Python: mockito-python There you can do virtually the same as in Java:

from mockito import when, mock, unstub

when(os.path).exists('/foo').thenReturn(True)

# or:
import requests  # the famous library
# you actually want to return a Response-like obj, we'll fake it
response = mock({'status_code': 200, 'text': 'Ok'})
when(requests).get(...).thenReturn(response)

# use it
requests.get('http://google.com/')

# clean up
unstub()

But:

  • clean up is required

  • the module seems not integrating standard unittest’s mocks . Maybe I am wrong, more investigation is required. I want to use mock.patch and have when-thenReturn construction working for it.

Similar articles

Mocking requests in python

There is a library requests-mock that helps testing network interactions.

Installation is easy: pip install requests_mock.

Usage example

Lets imagine you have a function get_data that queries an external API. We want to check that the function throws an exception if page is not found (http code 404).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pytest
import requests_mock


def test_function_throws_an_exception_if_page_not_found():
    with requests_mock.Mocker() as m:
        m.post(url, text=error_text, status_code=404)
        with pytest.raises(Exception):
            get_data(url)

m.post mocks requests for the predefined url and returns status_code=404 and some text message. One can also specify returned JSON data.

How to avoid mocking

Python is very too flexible with respect to types. Sometimes it plays agains the developers. If interface of the mocked class A changes you don’t notice that tests for a dependent class B are failing if you mock A in test_B.

Possible way to avoid it is to pass a factory to the dependent class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ClassName1: pass
  
class ClassName2: pass

class ClassFactory:
  def create_instance_class1(self, parameter):
    return ClassName1(parameter)
  
  def create_instance_class2(self, parameter):
    return ClassName2(parameter)
    
  
# class that we want to test
class ProductionClassWithFactory:
  def __init__(self, factory):
    self.factory = factory
    
  def foo(self, parameter1, parameter2):
    object1 = self.factory.create_instance_class1("some_initial_parameter1")
    intermediate_result = object1.run(parameter1)
    
    object2 = self.factory.create_instance_class2("some_initial_parameter2")
    final_result = object2.run(intermediate_result, parameter2)
    
    return final_result
    

Let’s test:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def test_foo():
    mocked_obj1 = MagicMock()
    mocked_obj1.run.return_value = "result1"
    mocked_obj2 = MagicMock()
    mocked_obj2.run.return_value = "result2"
    mocked_factory = MagicMock()
    mocked_factory.create_instance_class1.return_value = mocked_obj1
    mocked_factory.create_instance_class2.return_value = mocked_obj2

    actual_result = ProductionClassWithFactory(mocked_factory).foo("parameter1", "parameter2")

    # check final result:
    assert actual_result == "result2"
    # check calls arguments
    mocked_factory.create_instance_class1.assert_called_once_with("some_initial_parameter1")
    mocked_factory.create_instance_class2.assert_called_once_with("some_initial_parameter2")
    mocked_obj1.run.assert_called_once_with("parameter1")
    mocked_obj2.run.assert_called_once_with("result1", "parameter2")

Also you need to test the factory now. And it will require class monkey-patching. But in this case you put all monkey patching in one place.

Matching attributes for mocks

Keep in mind that standard mocks don’t care about non-existent atttributes. I use

1
@patch('module.ClassName1', autospec=True)

instead of

1
@patch('module.ClassName1')

That adds automatic checking of non-existent attributes and catches more errors. Let’s see where autospec=True can save your life.

1
2
3
4
5
6
7
8

class A:
  def foo():
    return "foo"
    
class B:
  def bar():
    return "bar" + A().foo()

here comes a test:

1
2
3
4
@mock.patch('module.A')
def test_bar(MockedA):
  MockedA.return_value.foo.return_value = "mocked-foo"
  assert B().bar() == "bar" + "mocked-foo"

At some point you decide to change class A. New version:

1
2
3
class A:
  def oops():
    return "oops"

You change tests for A but you can forget to fix B and tests for B. test_bar will still work because mocked A still have foo. Probably you can catch that error on integration tests level, but it is better to “fail fast”.

If you use @mock.patch('module.A', autospec=True), then you get an error (about non-existent attribute) after you change A on

1
MockedA.return_value.foo.return_value = "mocked-foo"

I think autospec=True has to be default behaviour of patch.

See also Mocking Objects in Python section “Danger: mocking non-existent attributes”

Alternative mocking libraries

Flexmock – extended/rebuilt clone of mocking library from Ruby. Abandoned project (last update in github in 2016).

Interesting syntax. Probably cleaner mocking sometimes. Example from docs for overriding new instances of a class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

# flexmock
flexmock(some_module.SomeClass).new_instances(some_other_object)
assertEqual(some_other_object, some_module.SomeClass())

# .......

# Mock
with mock.patch('somemodule.Someclass') as MockClass:
  MockClass.return_value = some_other_object
  assert some_other_object == some_module.SomeClass()

// Will it really work?

Mocking os.environ

@mock.patch.dict(os.environ, {'YOUR_VARIABLE': 'MOCKED_VALUE'})
def test_funciton():
  function_using_os_environment()

See also about testing in python

More about mocking and testing