Mock is not a crutch, mock is a specification

Among the many pseudo-ides orbiting TDDs, there is some disregard for test doubles, not least related to their ridiculous names.



They would call them somehow proudly, otherwise mok, stub, fake - a feeling that we don’t really lose anything if we don’t use it. (In contrast to "integration tests" and "real dependencies").



However, you can change the point of view. In the end, the mock not only and not so much supports the dependent component, but rather specifies the behavior of the dependency. And the “real” implementation is a certain current, possibly erroneous embodiment of our proud idea.



In this sense, every time we write



when(mockDependency.method(inputValue)).thenReturn(returnValue)



,



we document some component behavior.



Hence there are several consequences.



Mock objects are tightly connected with the actual specifications of the tests, both in vocabulary and in meaning. Both that and another focuses us on requirements and scenarios, but not on features of current implementation.



The fact that statements about a component to be wetted when using frameworks like Mockito are a bit distracting for us is scattered among client classes, and it’s hard to keep track of them. In this sense, explicit descendant classes with predefined behavior are preferable to mocks like Mockito, because they allow you to encapsulate scripts and view them at a glance.



Thus, script classes ValidOrderProviderStub, ExpiredOrderProviderStub, InvalidOrderIdException_OrderProviderStub, OnlyOnceOrderProvider and so on, which can be in the same package and used by all tests at once, are added to each OrderProvider class.



This includes Spy classes that are easy to implement yourself.



In addition, this approach is faster to execute and works without using reflection.



Thus, when reading the code, we can make a convention that the presence of mok confirms the possibility of a script, and its absence prohibits the existence of such a script. If we forbid client classes to write mockito expectations, then in the search for NullOrderProvider it turns out that there is no suitable mock, because null never returns, and therefore it makes no sense to test for this scenario.



If mok is a specification, then mok is a reflection of our plans for a component, and they can and should be written ahead of implementation.



If a mok is a specification, then it is hierarchical and reflects the behavior of the component depending on certain conditions.



Thus, our moki can multiply too much: unconditional moki always returning the same, as well as moki with internal conditions, such as forIdOne_returningOne_forIdTwo_ReturningTwo_OrderProvider. The last enumeration and you just need to create a FakeOrderProvider with the appropriate behavior, which should be synchronized with the actual implementation.



Accordingly, if a mok is a specification of a certain behavior, then the implementation of a component is nothing more than synchronizing the behavior of a component with its mokas.



And this, perhaps, is the main argument against mobs - the need to synchronize their behavior with the current real implementation, we’ll talk about this separately next time.



All Articles