Chapter 4. Decoupling Units with unittest.mock

Several times in the last couple of chapters, when faced with the problem of isolating tests from each other, I told you to just keep the problem in mind and said we'd deal with it in this chapter. Finally, it's time to actually address the problem.

Functions and methods that do not rely on the behavior of other functions, methods, or data are rare; the common case is to have them make several calls to other functions or methods, and instantiate at least one instance of a class. Every one of these calls and instantiations breaks the unit's isolation; or, if you prefer to think of it this way, it incorporates more code into the isolated section.

No matter how you look at it—as an isolation breaking or as expanding the isolated section—it's something you want to have the ability to prevent. Mock objects let you do this by taking the place of external functions or objects.

Using the unittest.mock package, you can easily perform the following:

  • Replace functions and objects in your own code or in external packages, as we did with time.time in Chapter 3, Unit Testing with doctest.
  • Control how replacement objects behave. You can control what return values they provide, whether they raise an exception, even whether they make any calls to other functions, or create instances of other objects.
  • Check whether the replacement objects were used as you expected: whether functions or methods were called the correct number of times, whether the calls occurred in the correct order, and whether the passed parameters were correct.

Mock objects in general

All right, before we get down to the nuts and bolts of unittest.mock, let's spend a few moments talking about mock objects overall.

Broadly speaking, mock objects are any objects that you can use as substitutes in your test code, to keep your tests from overlapping and your tested code from infiltrating the wrong tests. Thus, our fake time.time from Chapter 3, Unit Testing with doctest, was a mock object. However, like most things in programming, the idea works better when it has been formalized into a well-designed library that you can call on when you need it. There are many such libraries available for most programming languages.

Over time, the authors of mock object libraries have developed two major design patterns for mock objects: in one pattern, you can create a mock object and perform all of the expected operations on it. The object records these operations, and then you put the object into playback mode and pass it to your code. If your code fails to duplicate the expected operations, the mock object reports a failure.

In the second pattern, you can create a mock object, do the minimal necessary configuration to allow it to mimic the real object it replaces, and pass it to your code. It records how the code uses it, and then you can perform assertions after the fact to check whether your code used the object as expected.

The second pattern is slightly more capable in terms of the tests that you can write using it but, overall, either pattern works well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.38.92