Preamble

In the past few months I've been fortunate enough to write code using TDD. There a brief outline of my experiences using python Mock library help with adding test coverage for the code which is hard to test.

Lessons learned from using mock

Mocking internals of your application is hard, mostly because of dependencies on the other parts of the program, in this case object should be mocked if there is no other choice -- a much better approach is to just create object instances, this is especially true when creating database-backed objects. Integration tests of real database-backed object will verify application logic, as well as database relationships.

Mocks, however, are perfect when faking third party API responses -- there's no application interdependencies to be aware of, the test suite allows to execute code as is, without relying on third-party services or sending any data out, it codifies third party API responses as they are used by your application -- the latter point is especially helpful when a third party API changes between the releases, and any unexpected errors raised after an upgrade, can be quickly identified.

Knowing where to inject mock object

The most challenging part of mocking an object is to figure out where to inject a mocked object. The object needs to be mocked whenever its imported and used an not whenever it's defined. This makes sense because the purpose of the test is to check particular code path with a mock and not to mock that object everywhere. However, my initial assumption was to simply copy import statement from a source, which led to a lot of confusion.

In the following example, of guessing a number between 0 and 10. If the code was saved in a file main.py with import from random.randint

Debugging objects that don't cooperate with mock

The second-most challenging thing about mocking objects is timing -- especially the cases where an original instance of the object gets called before mock patch ever has a chance to replace the object.

In this particular case, going against pythonic way of importing code, keeping all the imports at the top of the file, is justified as this solves the problem, running import within patch section will do the trick.

In general there is no easy solution to this kind of problem, but a useful debugging technique would be to put a debug point before with declaration, just inside with declaration, and just before the declaration of the object to be mocked. Tracing the order of calls would give a good clue on what's wrong with the mock setup.

Most of the time, the problem is cause by the wrong assumption about the order of imports.

Mocked method call vs. mocked property

There's a difference between calling a mocked property and a method -- it's possible to just assign the value to a property, however for a function needs to be assigned the return value to the property return_value of that mock.