Search code examples
pythonunit-testingtestingmocking

Python mocking- understand the concept and necessity


First of all I really want to understand WHY should I use mocking (Python Mock library).

What's the difference between quickly making a small object for the need of testing something and using a mock object?

What are the advantages of mocking over other methods (I would really need some "live" examples to understand this one)? Is mocking essential in some cases?

Also: what's the difference between a mock object and magicmock object? How to they connect?

The second thing is WHAT/WHERE to mock.

Should I only mock, for example, the database query results? I mean: does the mocked data should always be a remote one?

tl;dr: Please explain the mocking concept in Python to someone who never used it in any language.


Solution

  • A mock object is meant to quickly and easily represent some complex object without having to manually go through and set up stubs for that object during a test. It's simply a useful utility for making writing tests a bit easier.

    As to what/where to mock, anything that is external to the module-under-test should be mocked. All you want to test is the current module code, not the code for some module that is being called by the module-under-test.

    A quick example is some code that uses the simplejson module.

    import simplejson
    
    def my_fn(args):
      return simplejson.dumps({'args': args})
    

    All you want to test is that the function my_fn makes the correct call to simplejson.dumps(), so you mock simplejson. You don't really care if the object passed to simplejson is converted to json correctly as testing that is in the scope of the simplejson module (which has it's own set of tests that you can run if you're so inclined).

    import working_code
    import mock
    
    @mock.patch('working_code.simplejson')
    def test_my_fn(mock_simplejson):
        working_code.my_fn('test-args')
        mock_simplejson.dumps.assert_called_with({'args': 'test-args'})
    

    Note that mock.patch is simply a nice way to inject and remove mocks for a particular test. After test_my_fn is run, working_code.simplejson returned to the state before the function was called. If that's confusing, you can think of the test example as:

    import working_code
    import mock
    
    def test_my_fn():
        mock_simplejson = mock.Mock()
        working_code.simplejson = mock_simplejson
        working_code.my_fn('test-args')
        mock_simplejson.dumps.assert_called_with({'args': 'test-args'})