I have a class that I want to test:
public class DivisionCalculator {
private final Validator validator;
private final MathProvider mathProvider;
private final ViewProvider viewProvider;
public DivisionCalculator(Validator validator, MathProvider mathProvider, ViewProvider viewProvider) {
this.validator = validator;
this.mathProvider = mathProvider;
this.viewProvider = viewProvider;
}
public String calculate(int dividend, int divisor) {
validator.validate(dividend, divisor);
List<DivisionStep> items = mathProvider.calculate(dividend, divisor);
DivisionResult result = DivisionResult.builder()
.dividend(dividend)
.divisor(divisor)
.finalResult(mathProvider.finalResult(items))
.items(items)
.build();
return viewProvider.provideView(result);
}
}
I have next following test class that works fine and pass the test using Mockito.mock(dependency.class)
public class DivisionCalculatorTest {
private ViewProvider viewProvider = Mockito.mock(ViewProvider.class);
private MathProvider mathProvider = Mockito.mock(MathProvider.class);
private Validator validator = Mockito.mock(Validator.class);
private DivisionCalculator divisionCalculator = new DivisionCalculator(validator, mathProvider, viewProvider);
DivisionResult makeDivision(int dividend, int divisor) {
List<DivisionStep> items = mathProvider.calculate(dividend, divisor);
return DivisionResult.builder()
.dividend(dividend)
.divisor(divisor)
.finalResult(mathProvider.finalResult(items))
.items(items)
.build();
}
@Test
void divisionCalculatorShouldSuccessfullyCallHisComponents() {
divisionCalculator.calculate(4, 2);
Mockito.verify(validator).validate(Mockito.anyInt(), Mockito.anyInt());
Mockito.verify(mathProvider).calculate(Mockito.anyInt(),Mockito.anyInt());
Mockito.verify(viewProvider).provideView(refEq(makeDivision(4, 2)));
}
}
But in order to follow the best practices working with mocks, I want to replace my Mockito.mock(dependency.class) with @Mock and @InjectMock annotations
public class DivisionTest {
@Mock
private Validator validator = new Validator();
@Mock
private ViewProvider viewProvider = new ViewProvider();
@Mock
private MathProvider mathProvider = new MathProvider();
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
}
@InjectMocks
private DivisionCalculator divisionCalculator = new DivisionCalculator(validator, mathProvider, viewProvider);
DivisionResult makeDivision(int dividend, int divisor) {
List<DivisionStep> items = mathProvider.calculate(dividend, divisor);
return DivisionResult.builder()
.dividend(dividend)
.divisor(divisor)
.finalResult(mathProvider.finalResult(items))
.items(items)
.build();
}
@Test
void divisionCalculatorShouldSuccessfullyCallHisComponents() {
divisionCalculator.calculate(4, 2);
Mockito.verify(validator).validate(4, 2);
Mockito.verify(mathProvider).calculate(4, 2);
Mockito.verify(viewProvider).provideView(refEq(makeDivision(4, 2)));
}
}
After following steps I got Error: Wanted but not invoked: Actually, there were zero interactions with this mock. I can't understand what am I missing here.
If you have annotations like @Mock
and @InjectMocks
you need to run the Junit test with a special extension in Junit 5:
@ExtendWith(MockitoExtension.class)
class MyTest {
@Mock
private Validator validator;
...
}
Also note that this extension creates mocks for you and injects the values into the annotated data fields. So technically its wrong to create the real objects like you've shown in the question:
public class DivisionTest {
@Mock
private Validator validator = new Validator(); // this is wrong, don't use "new"
@Mock
private ViewProvider viewProvider = new ViewProvider(); // this is wrong, don't use "new"
@Mock
private MathProvider mathProvider = new MathProvider(); // this is wrong, don't use "new"
You can follow this tutorial for more in-depth example and explanations.
Just for the sake of completeness, if you were using Junit 4, you could use:
@RunWith(MockitoJUnitRunner.class)
public class DivisionTest {...}