I don't know how to test a service with Mockito if it has two dependencies and the second dependency should work with the result of the first.
To better describe my problem, I wrote a small application for this: https://github.com/MartinHein-dev/mockito-example
With http://localhost:8080/countries one gets the result of three countries from https://restcountries.com/
I would be very happy if you could show me how the unit tests for de.example.mockito.service.CountryService.class would look like.
It feels wrong to continue with the mocked result of this.restCountiesClient.findCountriesByCode(countryCodes) and use it as a parameter in this.countryMapper.map(restCountryList), whose result is also mocked.
@ExtendWith(MockitoExtension.class)
class CountryServiceTest {
@Mock
RestCountriesClient client;
@Mock
CountryMapper mapper;
CountryService countryService;
List<RestCountry> restCountryList;
List<CountryDto> countryDtoList;
final String COUNTRY_CODES = "pe,at";
@BeforeEach
void setUp() throws Exception {
countryService = new CountryService(client, mapper);
restCountryList = List.of(
new RestCountry(new RestCountryName("Peru", "Republic of Peru")),
new RestCountry(new RestCountryName("Austria", "Republic of Austria"))
);
countryDtoList = List.of(
new CountryDto("Peru", "Republic of Peru"),
new CountryDto("Austria", "Republic of Austria")
);
}
@Test
void getAllCountries() {
given(client.findCountriesByCode(COUNTRY_CODES)).willReturn(restCountryList);
given(mapper.map(restCountryList)).willReturn(countryDtoList);
List<CountryDto> result = this.countryService.getAllCountries(COUNTRY_CODES);
assertEquals(2, result.size());
assertEquals("Peru", result.get(0).getCommonName());
assertEquals("Republic of Peru", result.get(0).getOfficialName());
assertEquals("Austria", result.get(1).getCommonName());
assertEquals("Republic of Austria", result.get(1).getOfficialName());
}
@Test
void getAllCountries2() {
given(client.findCountriesByCode(COUNTRY_CODES)).willReturn(restCountryList);
given(mapper.map(restCountryList)).willReturn(countryDtoList);
List<CountryDto> result = this.countryService.getAllCountries2(COUNTRY_CODES);
assertEquals(2, result.size());
assertEquals("Peru", result.get(0).getCommonName());
assertEquals("Republic of Peru", result.get(0).getOfficialName());
assertEquals("Austria", result.get(1).getCommonName());
assertEquals("Republic of Austria", result.get(1).getOfficialName());
}
@AfterEach
void tearDown() throws Exception {
reset(client, mapper);
}
Updated tests (2):
@Test
void getAllCountries() {
given(client.findCountriesByCode(COUNTRY_CODES)).willReturn(restCountryList);
given(mapper.map(restCountryList)).willReturn(countryDtoList);
this.countryService.getAllCountries(COUNTRY_CODES);
verify(client, times(1)).findCountriesByCode(COUNTRY_CODES);
verify(mapper, times(1)).map(restCountryList);
}
@Test
void getAllCountries2() {
given(client.findCountriesByCode(COUNTRY_CODES)).willReturn(restCountryList);
given(mapper.map(restCountryList)).willReturn(countryDtoList);
List<CountryDto> result = this.countryService.getAllCountries2(COUNTRY_CODES);
assertEquals("Other Name", restCountryList.get(0).getName().getCommon());
verify(client, times(1)).findCountriesByCode(COUNTRY_CODES);
verify(mapper, times(1)).map(restCountryList);
}
When you are testing getAllCountries
you are testing that the data flow works within the method, because the method itself doesn't do anything else but pass data between the dependencies. Therefore you do not need to set up populated objects for that method and you do not need to assert that the returned objects contain any particular populated data. You only need to verify that the expected (mocked) dependencies were called with the expected object references.
Testing the values you set up in th test belong to the unit test that target CountryMapper
.
For getAllCountries2
you would have to verify that the value in the test data has changed like you expect, but again no need to verify the values otherwise.