Search code examples
javajunitmockingmockitojunit5

How to test a service with two dependencies


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);
    }


Solution

  • 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.