In fact, to create a mock, you do not need an interface. You can create a mock of a class. What you will define is how you designed your solution. When using an interface and when using a class will enter into another discussion, which can be seen in the link shared by @Maniero.
I'll 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 not use the Data
class in the test. Doing this test will be difficult. One solution is to have the Data
class injected into the CalcularIdade
class. In this way you can also inject the mock of class 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;
}
}
The Data
is passed as a parameter to the CalcularIdade
class. Now I can pass the mock from class Data
to 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 will work perfectly fine. Note that this might not be the best solution, but the problem is in the design of the class, not the test.
If in the future I want to create another implementation for the class Data
, this can be done, just create a class that inherits from Data
. However, there will be a dependency of class CalcularIdade
with class Data
. Depending on the solution, this dependence may not be a problem. But if it is necessary to remove the dependency, an interface will provide just that. Ireir refactor my class so that the CalcularIdade
class is not implementation dependent:
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 stays:
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.
Maybe the library you are using will not allow you to create class mock. If this is the case, this may be a constraint for forcing the programmer to create a better design, but this is not a mock constraint conceptually speaking.