Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Junit How to mock namedParameterJdbcTemplate.query(" ", parameters,(ResultSet rs))

i am writing test cases for repository classes were i am not able to cover some of lines in repository classes. i need to achieve 85% of code coverage and its mandatory in my case,Please suggest me something

My actual method

public Map<String, String> getProductFamily(List<String> itmNms) {

        Map<String, String> productFamilyMap=new HashMap<String, String>();
        try {
        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        String sql = "some query";
        MapSqlParameterSource namedParameters = new MapSqlParameterSource();
        namedParameters.addValue("itmNms", itmNms);
        productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) -> {
            Map<String, String> productFamily = new HashMap<>();
            while (rs.next()) {
                productFamily.put(rs.getString("ITEMNAME"), rs.getString("PRODUCTFAMILY"));
            }
            return productFamily;
        });
        }catch (Exception e) {
            LOGGER.error("Exception in OracleRespository.getProductFamily : {}", e);
        }
        return productFamilyMap;
    }

Test case for above method

@Test
    public void getProductFamily() {

    List<String> itmNms = new ArrayList<String>();
    itmNms.add("A-SPK-NAMED-USER");
    oracleRepo.getProductFamily(itmNms);
    Map<String, String> mp = new HashMap<String, String>();
    Assert.assertNull(mp);

}

By writing above test cases i am able to cover code coverage till line no 6 below lines i am not able to cover due to below statements

productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) ->{}

Can some one suggest how can i achieve code coverage for above method as 100%.

like image 271
Viswanathan Avatar asked Apr 30 '20 05:04

Viswanathan


2 Answers

In cases like that, you need to "manually invoke" the code in lambda. This can be performed with Mockito.doAnswer(...) functionality of Mockito framework. The example (suitable for Mockito 2+):

Mockito.doAnswer(invocationOnMock -> {

    ResultSet resultSet = Mockito.mock(ResultSet.class);
    Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
    Mockito.when(resultSet.getString("ITEMNAME")).thenReturn(...);
    Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn(...);

    ResultSetExtractor<Map<String, String>> resultSetExtractor = 
        invocationOnMock.getArgument(2);
    return resultSetExtractor.extractData(resultSet);

}).when(namedParameterJdbcTemplate).query(
    Mockito.anyString(), 
    Mockito.any(MapSqlParameterSource.class), 
    Mockito.any(ResultSetExtractor.class)
);

Then you can verify productFamilyMap for populated key-value pair.

If you'd still have troubles with it, you can share your code (e.g. via Github) and I'll try to help you with it.

EDIT: Initially, I didn't notice the thing that NamedParameterJdbcTemplate is created manually with new, and it's kinda hard to mock it. In this case, it's better to refactor your production code a bit - you can create NamedParameterJdbcTemplate object as bean (like you probably did with raw JdbcTemplate) and then inject it into your class (and ofc remove the line where you're creating it with new). Then the things become trivial.

@Component
public class OracleRepository {

    private static final Logger LOGGER = LoggerFactory.getLogger(OracleRepository.class);

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate; //created as bean in configuration class

    public Map<String, String> getProductFamily(List<String> itmNms) {

        Map<String, String> productFamilyMap=new HashMap<String, String>();
        try {
            String sql = "some query";
            MapSqlParameterSource namedParameters = new MapSqlParameterSource();
            namedParameters.addValue("itmNms", itmNms);
            productFamilyMap = namedParameterJdbcTemplate.query(sql, namedParameters, (ResultSet rs) -> {
                Map<String, String> productFamily = new HashMap<>();
                while (rs.next()) {
                    productFamily.put(rs.getString("ITEMNAME"), rs.getString("PRODUCTFAMILY"));
                }
                return productFamily;
            });
        }catch (Exception e) {
            LOGGER.error("Exception in OracleRespository.getProductFamily : {}", e);
        }
        return productFamilyMap;
    }
}

The test class remains unchanged:

@RunWith(MockitoJUnitRunner.class)
public class OracleRepositoryTest {

    @InjectMocks
    private OracleRepository oracleRepo;

    @Mock
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Test
    public void getProductFamily() {
        List<String> itmNms = new ArrayList<>();
        itmNms.add("A-SPK-NAMED-USER");

        Mockito.doAnswer(invocationOnMock ->{
            ResultSet resultSet = Mockito.mock(ResultSet.class);
            Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
            Mockito.when(resultSet.getString("ITEMNAME")).thenReturn("A-SPK-NAMED-USER");
            Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn("SPKCLD");

            ResultSetExtractor<Map<String, String>> resultSetExtractor =
                    invocationOnMock.getArgument(2);
            return resultSetExtractor.extractData(resultSet);

        }).when(namedParameterJdbcTemplate).query(
                Mockito.anyString(),
                Mockito.any(MapSqlParameterSource.class),
                Mockito.any(ResultSetExtractor.class)
        );

        Map<String, String> productFamilyMap = oracleRepo.getProductFamily(itmNms);

        Assert.assertEquals("SPKCLD", productFamilyMap.get("A-SPK-NAMED-USER"));
    }
}
like image 121
amseager Avatar answered Nov 03 '22 12:11

amseager


The above answer is accurate and is working. I tried in my project too. Let me try to explain how this works.

We are saying when mocked JDBCTemplate query() method is called, then we want to invoke our own lambda expression with some mocking done, like we first create a mocked result set, and mocks some of its getString methods. Next we capture the third argument of mocked invocation which is result set extractor. Now from here we simply return this extractor extract data method with our mocked result set which will now be invoked. So in essence we are calling the original extract data method with our mocked result set.

Mockito.doAnswer(invocationOnMock -> {

    ResultSet resultSet = Mockito.mock(ResultSet.class);
    Mockito.when(resultSet.next()).thenReturn(true).thenReturn(false);
    Mockito.when(resultSet.getString("ITEMNAME")).thenReturn(...);
    Mockito.when(resultSet.getString("PRODUCTFAMILY")).thenReturn(...);

    ResultSetExtractor<Map<String, String>> resultSetExtractor = 
        invocationOnMock.getArgument(2);
    return resultSetExtractor.extractData(resultSet);

}).when(namedParameterJdbcTemplate).query(
    Mockito.anyString(), 
    Mockito.any(MapSqlParameterSource.class), 
    Mockito.any(ResultSetExtractor.class)
);
like image 2
tatushar3 Avatar answered Nov 03 '22 12:11

tatushar3