What types of tests (unit, functional, integration) work best with both?
Are there specific situations where it is more evident that it is worth using one or the other? Examples in any language are welcome.
To unit tests, mocks are the main choices and I also believe that fakes have their place. Commonly, in unit tests we are testing the behavior of a specific class and trying to ignore to the maximum the dependencies of this class.
In this act of "ignoring" their dependencies, we use the concept of mock to just make these dependencies behave in a certain way for each test.
With Mock
Let’s go to an example in Java, using the Mockite. Let’s say I want to test a service CreditoService
that depends on a ParametroService
and that consults a value called bonus
through this parameter class. In this case we are concerned to test the implementation of CreditoService
and we don’t care how Parametroservice works:
ParametroService parametroService = mock(ParametroService.class);
when(parametroService.getBonus(any(Empresa.class))).thenReturn("10.00");
CreditoService creditoService = new CreditoService(parametroService);
In the example above, I simply said to the parametroService
return "10.00" to me bonus
independent of empresa
the class receives as a parameter.
And the main difference that separates the Mock from a Fake is that with Mocks you can check and confirm if certain actions occurred with that Mock, because all these actions on top of the mock are recorded. In the previous example I created a mock and, although I defined an action expected by it, I did not check if it was actually called. Thus, I can also add a check that the bonus method of parametroService was called a certain number of times:
verify(parametroService, times(1)).getBonus(any(Empresa.class));
With Fake
In this same example, I could use a Fake
of ParametroService
. Let’s just say, depending on how much empresa
, I would like the ParametroService
do something different internally; any kind of logic on top of the company. For example, if the past company were a subsidiary, it would not have a bonus. We might have something like this then:
ParametroService parametroService = new ParametroServiceFake();
CreditoService creditoService = new CreditoService(parametroService);
Being ParametroServiceFake
an implementation of the same interface ParametroService
used by the actual implementation:
class ParametroServiceFake implements ParametroService {
String getBonus(Empresa empresa) {
if (empresa.isFilial()) {
return "0";
}
return "10.00";
}
}
In integration and functional tests this concept still exists, however the Fakes usually rule.
For example, in an integration test I can create a database in memory when making a Fake
of a repository class (Repository), in which data to be entered into a supposed database is stored in a Map
Fake internal, which can even provide search methods for this data "saved" in the repository.
I can do the same by making a Fake
of a class used to simulate the behavior of a WebService
:
class WebServiceFake implements WebService {
String consultarNomeCliente(String cpf) {
if (cpf.equals("123.123.123-11"))
return "Augusto Moraes Filho";
return "";
}
}
As you can see, it’s a functional implementation but far from the real one. But when you need to simulate certain behaviors of external systems, this technique comes in handy.
We can also use it in integration tests or functional mocks. At Spring this possibility exists and is well accessible through the @MockBean
, in which you can, in any integration/functional test, make a mock of any injectable class.
Which of them or in what sense they are better or worse when there are system changes, that is, to maintain the tests?
In general, mocks give less maintenance, mainly because of the context they are inserted: local, unit and isolated tests of the other tests.
Already the Fakes require functional code implementation. If we have code implemented, we have maintenance. And the problem only pióra when one Fake
needs to be "aligned" with other’s data Fake
.
Let’s take an example. Let’s say I want to do an integration test that uses data from two different external systems. One of them has the data of person and the other of the current account. We have then the person "Eduardo", of CPF "123.123.123-11" and with the current account "9876-0". So I can have one Fake
for each system. So far, so good.
Let’s get to the problem now. If I get Eduardo’s number on the first system, he needs to come. As if I do a search for the number in the second system, to have your current account. If, by any chance, one of the two sides doesn’t bring results by searching for the number, my test will fail. This alone will make me need to worry that both Fakes have CPF with person data and current account and that no one, in the future, "break" this link between both that exists in the actual usage scenario.
The original question did not mention this, but I have already answered what it is, the details are not yet: http://answall.com/a/36756/101
– Maniero
I think I can pass, but I found it curious how people are closing something and nobody dared to take a vote to close this and even others. Is playing with people’s limits :) People vote around even :)
– Maniero
@bigown Confess that I did it on purpose. : D But according to my update, I’m not asking about the definitions of concepts but the details of how to apply.
– utluiz
@utluiz here has a practical question on the subject. I believe she has a strong relationship with your question and can help to better understand the doubts between mocks and fakes.
– gato