Dependency injection versus mock

Another aspect that quickly appears in unit testing is how to manage dependencies. Let's consider the simple case of a function with an input and an output, as shown in the following figure:

Figure 10.2: A function data flow

Testing such a function is easy. It is very similar to the previous example. However, in many cases, a function (or a class method) has dependencies, as shown in the following figure:

Figure 10.3: A function with dependencies

In this case, a seemingly simple questions appears: what should be tested? Only the function? Or also the dependency function? Then how do we deal with the side effect? There is no unique answer to these questions. It always depends on what should be tested versus what is supposed to work. However, most of the time, the test boundary is set on the code of the package being written. Everything else is considered to be working, or tested somewhere else. So, if the dependency function is in the application package, then maybe it should really be called when testing the function. Otherwise, the call to the dependency function may be avoided, and replaced by a fake function.

There are two ways to do this: either with dependency injection or with mocks.

Mocks are a type of smart stub: they are replacement functions for the original function, and they can return whatever you choose, for each call. The unittest module contains a dedicated sub-module to implement mocks in Python. Mocks can be a very powerful way to handle dependencies in unit testing, but the way they are implemented in Python (compared to most other testing frameworks) is not easy to comprehend, and sometimes they can be difficult to configure correctly.

Dependency injection takes the issue the other way: instead of directly calling a dependency function, the tested function should receive it as an input parameter. This is shown in the following figure:

Figure 10.4: Dependency injection

The big advantage of testing such a pure code is the fact that the function now has the same signature as that on Figure 10.2. The drawback is that this can have a big impact on the design of the application.

Depending on the situation, dependency injection or mocks will be used to stub dependencies that must not be used in a test. However, when writing functional code, this situation occurs only with code external to the package. Moreover, splitting pure code and side-effects should further reduce the situations where dependency code should be stubbed.

Note that stubbing external code is not always necessary. As an example, stubbing the json.loads function to test the configuration parser of the audio transcoder would be overkill: it is a dependency, but there is no reason to stub it in any way. 

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

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