In fact, to create a mock, you don’t need an interface. You can create a mock of a class. What will define is how you designed your solution. When you use an interface and when you use a class you will enter another discussion, which can be seen in the link shared by @Maniero.
I will try to explain with examples.
Imagine you have this class:
public class CalcularIdade {
public int calcular(int anoNascimento) {
Data data = new Data();
int anoAtual = data.getAno();
return anoAtual - anoNascimento;
}
}
Now imagine that for some reason you can’t use the class Data
on the test. Taking this test will be difficult. One solution is to make that class Data
injected into the class CalcularIdade
. This way it will also be possible to inject the class mock Data
. A first alternative would be this:
public class CalcularIdade {
private Data data;
public CalcularIdade(Data data) {
this.data = data;
}
public int calcular(int anoNascimento) {
int anoAtual = data.getAno();
return anoAtual - anoNascimento;
}
}
To Data
is passed as parameter to the class CalcularIdade
. Now I can pass the class mock Data
for the class CalcularIdade
. The actual application would look like this:
public static void main(String args[]) {
CalcularIdade calcularIdade = new CalcularIdade(new Data());
int idade = calcularIdade.calcular(1990);
System.out.println(idade);
}
The test would look like this:
@Test
public void calcularTest() {
Data dataStub = mock(Data.class);
when(dataStub.getAno()).thenReturn(2018);
int anoNascimento = 1990;
CalcularIdade calcularIdade = new CalcularIdade(dataStub);
int result = calcularIdade.calcular(anoNascimento);
assertEquals(result, 28);
}
I made a mock of a class and it will work perfectly well. Note that this may not be the best solution, but the problem is in the design of the class and not in the test.
If in the future I wish to create another implementation for the class Data
, this can be done by creating a class that inherits from Data
. However, there will be a class dependency CalcularIdade
with class Data
. Depending on the solution, this dependency may not be a problem. But if it is necessary to remove the dependency, an interface will provide just that. It will refactor my class to that class CalcularIdade
does not depend on implementation:
public class CalcularIdade {
private IData iData;
public CalcularIdade(IData data) {
this.iData = data;
}
public int calcular(int anoNascimento) {
int anoAtual = iData.getAno();
return anoAtual - anoNascimento;
}
}
My test:
@Test
public void calcularTest() {
IData dataStub = mock(IData.class);
when(dataStub.getAno()).thenReturn(2018);
int anoNascimento = 1990;
CalcularIdade calcularIdade = new CalcularIdade(dataStub);
int result = calcularIdade.calcular(anoNascimento);
assertEquals(result, 28);
}
If tomorrow I want a new implementation of Idata, my application is:
public static void main(String args[]) {
CalcularIdade calcularIdade = new CalcularIdade(new NovaData());
int idade = calcularIdade.calcular(1990);
System.out.println(idade);
}
Could it be done with class? Yes. But there would be a coupling between Data
and CalcularIdade
, with the interface this coupling some.
Perhaps the library you are using does not allow you to create class mock. If this is the case, this may be a constraint to force the programmer to create a better design, but this is not a constraint of the mock conceptually speaking.
I don’t know about him specifically, but it has to do with https://answall.com/q/86484/101.
– Maniero
Related: https://stackoverflow.com/questions/9226323/mocking-a-class-vs-mocking-its-interface
– Piovezan