Category: Designing For Testability
-
The Hexagonal Architecture and mocks as a design technique
Now that you know about the Hexagonal Architecture and the idea of ports and adapters, we can talk about mocks as a design technique. In a nutshell, whenever mockists develop a feature (or a domain object) and notice that they need something from another place, they let a port emerge. As we saw, the port…
-
Static methods, singletons, and testability
As we have seen, static methods adversely affect testability. Therefore, a good rule of thumb is to avoid creating static methods whenever possible. Exceptions to this rule are utility methods, which are often not mocked. If your system has to depend on a specific static method, perhaps because it comes with the framework your software…
-
Private methods and testability
A common question among developers is whether to test private methods. In principle, testers should test private methods only through their public methods. However, testers often feel the urge to test a particular private method in isolation. A common reason for this feeling is the lack of cohesion or the complexity of the private method.…
-
Complex conditions and testability
We have seen in previous chapters that very complex conditions (such as an if statement composed of multiple boolean operations) require considerable effort from testers. For example, we may devise too many tests after applying boundary testing or condition + branch coverage criteria. Reducing the complexity of such conditions by, for example, breaking them into multiple smaller…
-
The coupling of the class under test
In a world of cohesive classes, we combine different classes to build large behaviors. But doing so may lead to a highly coupled design. Excessive coupling may harm evolution, as changes in one class may propagate to other classes in ways that are not clear. Therefore, we should strive for classes that are coupled as…
-
The cohesion of the class under test
Cohesion is about a module, a class, a method, or any element in your architecture having only a single responsibility. Classes with multiple responsibilities are naturally more complex and harder to comprehend than classes with fewer responsibilities. So, strive for classes and methods that do one thing. Defining what a single responsibility means is tricky…
-
Designing for testability in the real world
Writing tests offers a significant advantage during development: if you pay attention to them (or listen to them, as many developers say), they may give you hints about the design of the code you are testing. Achieving good class design is a challenge in complex object-oriented systems. The more help we get, the better. The buzz about…
-
Dependency via class constructor or value via method parameter?
A very common design decision is whether to pass a dependency to the class via constructor (so the class uses the dependency to get a required value) or pass that value directly to the method. As always, there is no right or wrong way. However, there is a trade-off you must understand to make the…
-
Example 2: Observing the behavior of void methods
When a method returns an object, it is natural to think that assertions will check whether the returned object is as expected. However, this does not happen naturally in void methods. If your method does not return anything, what will you assert? It is even more complicated if what you need to assert stays within the method.…
-
Example 1: Introducing methods to facilitate assertions
Take another look at the processAll() method and its test, in listings 7.2 and 7.5. Most of what its test asserts is the interaction with the ports. Such assertions are easily done, and we did not need much more than basic Mockito. Now, let’s look closer at one specific assertion: verify(someCart).markAsReadyForDelivery(someDate);. The someCart instance of ShoppingCart is not a mock but a spy.…