junit如何测试ResultSet结果集

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了junit如何测试ResultSet结果集相关的知识,希望对你有一定的参考价值。

参考技术A - -、junit跟ResultSet没有任何关系、
junit只是一个容器,是独立于main函数外的一个测试的工具,你只要继承了TestCase 然后在你要测试的方法名上右键- Run 就会选择运行的工具,这时候会有junit本回答被提问者采纳

Junit 如何模拟 namedParameterJdbcTemplate.query(" ", parameters,(ResultSet rs))

【中文标题】Junit 如何模拟 namedParameterJdbcTemplate.query(" ", parameters,(ResultSet rs))【英文标题】:Junit How to mock namedParameterJdbcTemplate.query(" ", parameters,(ResultSet rs)) 【发布时间】:2020-08-14 09:56:06 【问题描述】:

如果我无法涵盖存储库类中的某些行,我正在为存储库类编写测试用例。我需要实现 85% 的代码覆盖率,并且在我的情况下是强制性的,请给我一些建议

我的实际方法

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


通过编写上面的测试用例,我能够覆盖代码覆盖率,直到第 6 行以下由于以下语句而我无法覆盖的行

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

有人可以建议我如何将上述方法的代码覆盖率实现为 100%。

【问题讨论】:

请分享测试课。你会自动连接你的测试类吗?你用SpringRunner吗? 【参考方案1】:

在这种情况下,您需要“手动调用” lambda 中的代码。这可以通过Mockito 框架的Mockito.doAnswer(...) 功能来执行。示例(适用于 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)
);

然后您可以验证productFamilyMap 是否已填充键值对。

如果您仍然遇到问题,您可以分享您的代码(例如通过 Github),我会尽力帮助您。

编辑:最初,我没有注意到NamedParameterJdbcTemplate 是用new 手动创建的,而且它有点难以模拟。在这种情况下,最好稍微重构您的生产代码 - 您可以将 NamedParameterJdbcTemplate 对象创建为 bean(就像您可能对原始 JdbcTemplate 所做的那样),然后将其注入您的类(并删除您所在的行)用new 重新创建它)。然后事情就变得微不足道了。

@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;
    

测试类保持不变:

@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"));
    

【讨论】:

我正面临错误,例如在测试类中检测到不必要的存根:OracleRepositoryTest 清洁和可维护的测试代码需要零个不必要的代码。也请给我 productFamilyMap 我可以用一些值创建我自己的地图,或者它应该是一个模拟对象 productFamilyMap 将由.thenReturn(...) 方法中指定的数据自动填充(resultSetExtractor.extractData(resultSet) 是在 lambda 引擎下调用的代码)。关于不必要的存根——我想测试类中可能会出现一堆问题,而且很难像这样找到它们。最好在这里或在 github 上拥有minimal reproducible example 以便更快地找到它。 感谢@amseager 的快速回复,请参考link 我在when(namedParameterJdbcTemplate).query(); 行遇到了不必要的存根问题 @Viswanathan 的链接对我帮助最大。【参考方案2】:

以上答案是准确的并且有效。我也在我的项目中尝试过。 让我试着解释一下这是如何工作的。

我们是说当模拟的 JDBCTemplate query() 方法被调用时,我们想要调用我们自己的 lambda 表达式并完成一些模拟,就像我们首先创建一个模拟结果集,并模拟它的一些 getString 方法。接下来我们捕获模拟调用的第三个参数,即结果集提取器。现在从这里我们简单地返回这个提取器提取数据方法和我们现在将被调用的模拟结果集。 所以本质上,我们使用模拟结果集调用原始提取数据方法。

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

【讨论】:

以上是关于junit如何测试ResultSet结果集的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 中使用 Mockito 模拟结果集并填充它

JAVA初学者ResultSet结果集报错

Java 中Apache-DBUtils类的使用详解

普通结果集ResultSet和离线结果集RowSet

Mockito - 模拟ResultSet

ResultSet 异常 - 在结果集开始之前