Search code examples
spring-bootunit-testingjunitmockitojunit5

How to do Junit testing with mockito for service layer and Dao layer with NamedParameterJdbcTemplate?


I have layered architecture in the spring boot project like Controller -> Service -> Dao. I have to write unit test cases to check all functionality. I want to write unit test cases for service layer and Dao layer. Service layer internally calls 3 DAO methods.

Currently, I am trying to write test cases for service layer and its internally calling Dao methods but its giving null pointer exception.

My Testing code is as below. After executing this test case, I am getting null pointer exception on 'namedParameterJdbcTemplate.queryForObject(existingUtQuery, namedParameters, GeneratorRowMapper)' in GeneratorRepository Dao class. I have initialized 'generatorRepository = new GeneratorRepository() and uTService = new UTService()' in Response1 test method of test class otherwise it give null pointer exception.

@ExtendWith(MockitoExtension.class)
public class GenerationApplicationTests {

    @InjectMocks
    private GeneratorRepository generatorRepository;
    
    @Mock
    private  NamedParameterJdbcTemplate namedParameterJdbcTemplate;
    
    @Autowired
    private UTService uTService;
    
    
    @Test
    public void Response1() throws Exception {

        generatorRepository = new GeneratorRepository();
        uTService = new UTService();

        Request request = new Request("12345678", "F", 0, "A");
        Generator generator = new Generator("12345678.F","3er45FGTYH");

        System.out.println("utService  :  "+utService);

        when(generatorRepository.findExisting(request)).thenReturn(generator);
        Response response = uTService.getUT(Request);
        assertEquals("12345678.F", response.getValue());
        assertEquals("3er45FGTYH", response.getPre());
    }

}

Service Class

@Service
public class UTService {

    @Autowired
    private GeneratorRepository generatorDAO;

    @Autowired
    private TableFunctionRepository tableFunctionRepository;

    public Response getUT(Request request) throws UTNotFoundException {
        Response response = new Response();
        Generator existing = new Generator();
        TableFunction tableData = new TableFunction();
        Generator new = new Generator();
        Reasons exclusionInclusion = null;
        existing = generatorDAO.findExisting(request);
        if (existing != null) {
            response.setResponse(existing);
            return response;
        }
        tableData = tableFunctionRepository.callTableValuedFunction(request);
        if (tableData == null) {
            String exceptionMessage = Request + " not found in database";
            throw new UTNotFoundException(exceptionMessage);
        } else {
            new.setInfo(request.getId(), request.getSubId(), request.getRevisionNo(), request.getState());
            exclusionInclusion = checkExclusionInclusionCriteria(tableData);
            new.setExclusionInclusionReasonId(exclusionInclusion.getReasonCode());
            new = generateUT(tableData, exclusionInclusion, new);
            response.setResponse(new);
            generatorDAO.saveUT(new);
        }
        log.info("finished getUT");
        return response;

    }
    
}

Dao Classes

@Repository
public class GeneratorRepository 
{
    @Value("${sql.saveutiQuery}")
    private String saveUTQuery;

    @Value("${sql.existingUtiQuery}")
    private String existingUtQuery;


    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Autowired
    private BeanPropertyRowMapper<Generator> generatorRowMapper;

    public int saveUT(Generator generator) {
        int rowsAffected = 0;
        try {
            SqlParameterSource paramSource = new BeanPropertySqlParameterSource(generator);
            rowsAffected = namedParameterJdbcTemplate.update(saveUTQuery, paramSource);
        } catch(Exception e) {
            throw e;
        }
        return rowsAffected;
    }

    public Generator findExistingUT(Request request) {
        Generator ut = new Generator();
        try {
            SqlParameterSource namedParameters = new MapSqlParameterSource()
                    .addValue("No", request.getId(), Types.VARCHAR)
                    .addValue("Sub", request.getSubId(), Types.VARCHAR)
                    .addValue("Revision", request.getRevision(), Types.INTEGER)
                    .addValue("state", request.getState(), Types.VARCHAR);
            ut = namedParameterJdbcTemplate.queryForObject(existingUtQuery, namedParameters, GeneratorRowMapper);
        } catch (Exception e) {
            throw e;
        }
        return ut;
    }
}


@Repository
public class TableFunctionRepository {

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Autowired
    private BeanPropertyRowMapper<TableFunction> tableFunctionRowMapper;

    @Value("${sql.DetailsQuery}")
    private String tableDetailsQuery;

    public TableFunction callTableValuedFunction(Request request) {
        TableFunction tableDetails = new TableFunction();
        try {
            SqlParameterSource namedParameters = new MapSqlParameterSource()
                    .addValue("No", request.getId(), Types.VARCHAR)
                    .addValue("Sub", request.getSub(), Types.VARCHAR)
                    .addValue("Revision", request.getRevision(), Types.INTEGER)
                    .addValue("state", request.getState(), Types.VARCHAR);

            tableDetails = namedParameterJdbcTemplate.queryForObject(tableDetailsQuery, namedParameters, tableFunctionRowMapper);
        } catch (Exception e) {
            throw e;
        }       
        return tableDetails;
    }
}

What approach should I follow to write test cases using junit and mockito to cover both service and Dao layer.


Solution

  • First of all, When you have @autowired variables it means that you need to @mock them if you want to get mocked object.

    Then if you test the Service class then you need to use @InjectMocks for this service. It means that you are creating a mocked service for testing and injecting to him all mocks that you mocked with @mock annotation or with another way.

    Here updated version of your test:

    @ExtendWith(MockitoExtension.class)
    public class UTServiceTest {
    
        @InjectMocks
        private UTService uTService;
    
        @Mock
        private GeneratorRepository generatorRepository;
    
        @Mock
        private TableFunctionRepository tableFunctionRepository;
    
        @Test
        public void testGetUT_whenExistingGeneratorFound_returnResponse() throws UTNotFoundException {
            // Arrange
            Request request = new Request("12345678", "F", 0, "A");
            Generator existingGenerator = new Generator("12345678.F", "3er45FGTYH");
            Response expectedResponse = new Response(existingGenerator);
    
            // Mock generatorRepository to return an existing generator for the given request
            when(generatorRepository.findExisting(request)).thenReturn(existingGenerator);
    
            // Act
            Response actualResponse = uTService.getUT(request);
    
            // Assert
            assertEquals(expectedResponse, actualResponse);
        }
    

    And you can test your exception throwing

    @Test
        public void testGetUT_whenTableDataNotFound_throwUTNotFoundException() throws UTNotFoundException {
            Request request = new Request("12345678", "F", 0, "A");
            when(tableFunctionRepository.callTableValuedFunction(request)).thenReturn(null);
    
            assertThrows(UTNotFoundException.class, () -> uTService.getUT(request));
        }
    

    Also, you can test the one more case when all is passing by adding this lines to first test case but write 3rd test for it:

     when(generatorRepository.findExisting(request)).thenReturn(null);
    
            // Mock tableFunctionRepository to return tableData for the given request
            when(tableFunctionRepository.callTableValuedFunction(request)).thenReturn(tableData);
    
            // Mock the generateUT method to return the expectedGenerator
            when(uTService.generateUT(tableData, exclusionInclusion, any(Generator.class)))