What are the differences between mocks and fakes?

10

When we implement testing on a software, Mocks and Fakes are two techniques or approaches to isolate code being tested from the rest of the system.

Without going into the subject:

  • Mocks are like empty shells with predefined behaviors, such as: when calling method X, return the value Y
  • Fakes are fully or partially functional objects implemented especially for tests intended to simulate the operation of the system, but in a relatively isolated way.

But what would be the relevant differences between the two concepts for the correct implementation of tests? Some relevant aspects:

  • What types of tests (unit, functional, integration) work best with both?
  • Are there specific situations where it is most obvious that it is worth using one or the other? Examples in any language are welcome.
  • Which one or in what sense are they better or worse when there are changes in the system, that is, to maintain the tests?
  

Update: I do not want the definition of concepts, this is already in the question. What I want are differences in the application, as described in the topics above, but not limited to them. Someone who has used both should have an idea of what I'm talking about.

    
asked by anonymous 02.02.2016 / 02:09

1 answer

2
  

What types of tests (unit, functional, integration) work best with both?

     

Are there specific situations where it is most obvious that it is worth using one or the other? Examples in any language are welcome.

For unit testing , mocks are the top 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 the dependencies of this class to the maximum.

In this act of "ignoring" your dependencies, we use the mock concept to only make these dependencies behave in a certain way for each test.

With Mock

Let's look at an example in Java, using Mockito . Let's say I want to test a CreditoService service that depends on a ParametroService and which queries a value called bonus through this parameter class. In this case we are concerned with testing the implementation of CreditoService and we do not 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 told parametroService to return "10.00" when querying bonus independent of empresa that the class receives as a parameter.

And the main difference that separates Mock from a Fake is that with Mocks you can check and confirm if certain actions occurred with that Mock, because all of these actions on top of the mock are recorded. In the previous example I created a mock and although I have defined an action expected by it, I did not check if it was actually called. So, I can also add a check that the bonus method of the parametroService has been 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 say, depending on the value I get from empresa , I'd like ParametroService to do something different internally; any kind of logic on top of the company. For example, if the past company was an affiliate, it would not have a bonus. We can 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, but Fakes often reign.

For example, in an integration test I can create an in-memory bank by doing a Fake of a repository class, in which the data to be inserted into a so-called database are stored in% internal% of Fake, which can even provide search methods for this "saved" data in the repository.

I can do the same also by making a Map of a class used to simulate the behavior of a Fake :

 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 is a functional implementation, however, 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 in integration tests or functional mocks. In Spring this possibility exists and is very accessible through WebService , in which you can, in any integration / functional test, make a mock of any injectable class.

  

Which one or in what sense are they better or worse when there are changes in the system, that is, to maintain the tests?

In general, mocks give less maintenance, mainly because of the context they are inserted: local, unitary and isolated tests of the other tests.

Fakes require functional code implementation. If we have code implemented, we have maintenance . And the problem only happens when a @MockBean has to be "aligned" with the data of another 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 has the person data and the other has the current account. Then we have the person "Eduardo", CPF "123.123.123-11" and the checking account "9876-0". So, I can have a Fake for each system. So far so good.

Let's get into the problem now. If I search for Eduardo's CPF on the first system, he must come. As if I do a search for the CPF on the second system, to have your checking account. If, by chance, one of the two sides does not bring results when searching for the CPF, my test will fail. This in itself will make me need to worry that both Fakes have CPF with person and current account data and that no one in the future will "break" this connection between both that exists in the actual scenario of use.

    
04.07.2018 / 00:58