Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

patch multiple methods from different modules (using Python mock)

The structure of my modules:

foo: 
  - load()  # from DB


bar:
  - check() # with user
  - take_action() 

I want to test take_action (which basically loads values and checks with user before taking action) by mocking both load and check.

Here are the mocks:

mock_load  = Mock(side_effects=[<>, <>, <>]) # different data sets
mock_check = Mock(return_value=True)  # User approval

How do I use patch.multiple to achieve this using Python 2.6?

with patch.multiple(??):
    # proceed to test
    take_action
like image 538
Shyam Sunder Avatar asked Dec 03 '14 11:12

Shyam Sunder


People also ask

What is the difference between mock and MagicMock?

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 patch in Python mock?

patch() unittest. 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.


1 Answers

The short answer is no you cannot use patch.multiple() to do it. As described in patch.multiple all arguments will be applied to all created mocks and all arguments MUST be attribute of the same object. You MUST do it one of the time by single patch calls.

Unfortunately you are using python 2.6 so you can use just nested fron contextlib like pointed in python: create a "with" block on several context managers and Multiple context `with` statement in Python 2.6.

Maybe the more cleaner and simple way to do it is use @patch as decorator:

@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
def test_mytest(mock_check,mock_load):
    take_action()
    assert mock_load.called
    assert mock_check.called

If you need it in all tests of a test class you can decorate the class and use the mocks in all test methods:

@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
class TestMyTest(unittest.TestCase)
    def test_mytestA(self,mock_check,mock_load):
        take_action()
        self.assertTrue(mock_load.called)
        self.assertTrue(mock_check.called)

    def test_mytestA(self,mock_check,mock_load):
        mock_check.return_value = False
        take_action()
        self.assertTrue(mock_load.called)
        self.assertTrue(mock_check.called)

Finally you can do it by using with and contextlib and the first example become:

from contextlib import nested

with nested(patch("foo.load",side_effects=["a","b","c"]), patch("bar.check",return_value=True)) as (mock_load, mock_check):
    take_action()
    assert mock_load.called
    assert mock_check.called

... Or nest it by hand ....

with patch("foo.load",side_effects=["a","b","c"]) as mock_load:
    with patch("bar.check",return_value=True)) as mock_check:
        take_action()
        assert mock_load.called
        assert mock_check.called

My feel is that decorators are the most readable and simple to use.

like image 178
Michele d'Amico Avatar answered Sep 20 '22 14:09

Michele d'Amico