Java 如何编写使用匿名内部类(如PreparedStatementSetter)的类的JUnit测试?

Java 如何编写使用匿名内部类(如PreparedStatementSetter)的类的JUnit测试?,java,spring,junit,spring-test,spring-test-mvc,Java,Spring,Junit,Spring Test,Spring Test Mvc,我有一个使用匿名内部类(new PreparedStatementSetter())的类,它使用PreparedStatementSetter在查询中设置值,并返回对象的列表 问题是我不确定如何为这种方法编写单元测试。 当我的单元测试到达调用getJdbcTemplate().query(…)的行时,它会跳到行的末尾,discAuditLogList值返回null,单元测试完成时没有任何错误。 当我查看IntelliJ中的代码覆盖率时,所有ps.setString语句从未执行过 ProductL

我有一个使用匿名内部类(
new PreparedStatementSetter()
)的类,它使用
PreparedStatementSetter
在查询中设置值,并返回对象的
列表

问题是我不确定如何为这种方法编写单元测试。 当我的单元测试到达调用
getJdbcTemplate().query(…)
的行时,它会跳到行的末尾,
discAuditLogList
值返回
null
,单元测试完成时没有任何错误。 当我查看
IntelliJ
中的代码覆盖率时,所有
ps.setString
语句从未执行过

ProductLogDao.java

//bunch of setter getters here

public List<ProductLogDTO> getProductLogDetail(RequestDTO requestDTO) throws SQLException, DataAccessException {
    logger.info("Inside ProductLogDao.getProductLogDetail" + requestDTO);
    List<ProductLogDTO> productLogList = null;
    final Map<String, String> requestParamMap = requestDTO.getParameters();
    if ("custom".equalsIgnoreCase(requestParamMap.get("recordtype"))) {
        ProductLogList = getProductCustomDetail(senderFeeSql, requestParamMap);
        return ProductLogList;
    }
    return productLogList;
}


public List<ProductLogDTO> getProductCustomDetail(String senderFeeSql,
                                                          final Map<String, String> requestParamMap) throws SQLException {
    @SuppressWarnings("unchecked")
    List<ProductLogDTO> productLogList = getJdbcTemplate().query(senderFeeSql, new PreparedStatementSetter() {
        public void setValues(PreparedStatement ps) throws SQLException {
            /***************************************************************************************************************/
            //FOLLOWING STATMENTS NEVER GET EXECUTED BECAUSE THEY ARE WITHIN ANONYMOUS CLASS (new PreparedStatementSetter())
            /***************************************************************************************************************/
            ps.setString(1, requestParamMap.get("startDfId"));
            ps.setString(2, requestParamMap.get("startDfId"));
            ps.setString(3, requestParamMap.get("chanelId"));
            ps.setString(4, requestParamMap.get("chanelId"));
            ps.setString(5, requestParamMap.get("fromDateTime"));
            ps.setString(6, requestParamMap.get("toDateTime"));
            ps.setString(7, requestParamMap.get("fromDateTime"));
            ps.setString(8, requestParamMap.get("toDateTime"));
        }
    }, new ProductCustomRowMapper());

    if (null != productLogList && (productLogList.size() > 0)) {
        productLogList.get(0).setRecordtype("custom");
        productLogList.get(0).setRecordsFetched(productLogList.get(0).getRecordsFetched());
    }
    return productLogList;
}
//这里有一堆setter getter
公共列表getProductLogDetail(RequestDTO RequestDTO)抛出SQLException、DataAccessException{
logger.info(“insideproductlogdao.getProductLogDetail”+requestDTO);
List productLogList=null;
final Map requestParamMap=requestDTO.getParameters();
if(“custom”.equalsIgnoreCase(requestParamMap.get(“recordtype”)){
ProductLogList=getProductCustomDetail(senderFeeSql,requestParamMap);
返回产品日志列表;
}
返回产品日志列表;
}
公共列表getProductCustomDetail(字符串senderFeeSql,
final Map requestParamMap)引发SQLException{
@抑制警告(“未选中”)
List productLogList=getJdbcTemplate().query(senderFeeSql,new PreparedStatementSetter()){
public void setValues(PreparedStatement ps)引发SQLException{
/***************************************************************************************************************/
//以下语句永远不会执行,因为它们位于匿名类(new PreparedStatementSetter()中)
/***************************************************************************************************************/
ps.setString(1,requestParamMap.get(“startFID”);
ps.setString(2,requestParamMap.get(“startFID”);
ps.setString(3,requestParamMap.get(“chanelId”);
ps.setString(4,requestParamMap.get(“chanelId”);
ps.setString(5,requestParamMap.get(“fromDateTime”);
ps.setString(6,requestParamMap.get(“toDateTime”);
ps.setString(7,requestParamMap.get(“fromDateTime”);
ps.setString(8,requestParamMap.get(“toDateTime”);
}
},新产品CustomRowMapper());
if(null!=productLogList&&(productLogList.size()>0)){
productLogList.get(0.setRecordtype(“自定义”);
productLogList.get(0).setRecordsFetched(productLogList.get(0).getRecordsFetched());
}
返回产品日志列表;
}
ProductLogDaoTest.java

public class ProductLogDaoTest {

    ProductLogDao instance = new ProductLogDao();
    RequestDTO requestDTO = Mockito.mock(RequestDTO.class);
    JdbcTemplate jdbcTemplate = Mockito.mock(JdbcTemplate.class);
    Map<String, String> requestParamMap = new HashMap<>();

    @Before
    public void setUp() throws Exception {
        instance.setJdbcTemplate(jdbcTemplate);
    }

    @Test
    public void getProductLogDetail_W_Custom() throws SQLException, DataAccessException {
        when(requestDTO.getParameters()).thenReturn(requestParamMap);
        requestParamMap.put("recordtype", "custom");
        assertNotNull(instance.getProductLogDetail(requestDTO));
    }

}
公共类ProductLogDaoTest{
ProductLogDao实例=新的ProductLogDao();
RequestDTO RequestDTO=Mockito.mock(RequestDTO.class);
JdbcTemplate=Mockito.mock(JdbcTemplate.class);
Map requestParamMap=新HashMap();
@以前
public void setUp()引发异常{
setJdbcTemplate(jdbcTemplate);
}
@试验
public void getProductLogDetail_W_Custom()抛出SQLException、DataAccessException{
when(requestDTO.getParameters()).thenReturn(requestParamMap);
requestParamMap.put(“记录类型”、“自定义”);
assertNotNull(instance.getProductLogDetail(requestDTO));
}
}

您模拟了
JdbcTemplate
,但没有模拟
JdbcTemplate
方法。这就是它返回null的原因

您可以使用某种类型的测试数据库。如果是Spring,您可以使用嵌入式的:。有关测试期间模拟数据库的更多信息,请参阅此问题:


无论哪种方法,您都可以检查您的
query()
方法是否使用Mockito的
verify()
方法调用。

您模拟了
JdbcTemplate
,但没有模拟您的
JdbcTemplate
方法。这就是它返回null的原因

您可以使用某种类型的测试数据库。如果是Spring,您可以使用嵌入式的:。有关测试期间模拟数据库的更多信息,请参阅此问题:


无论哪种方法,您都可以检查您的
query()
方法是否使用Mockito的
verify()
方法调用。

切勿在依赖对象内部创建如下对象实例:
new PreparedStatementSetter()
它很难连接这个类实例,您无法将其更改为模拟。 始终执行以下操作之一:

  • 通过工厂对象方法调用(myBusinessFactoryInstance.createPreparedStatementSetter)创建这些类实例。如果更改工厂,则可以创建模拟

  • 在Spring中,您可以通过构造函数或setter方法注入此依赖项


  • 如果您这样做,您的测试将像一个符咒。

    永远不要像这样在依赖对象中创建对象实例:
    new PreparedStatementSetter()
    它很难连接这个类实例,您无法将其更改为模拟。 始终执行以下操作之一:

  • 通过工厂对象方法调用(myBusinessFactoryInstance.createPreparedStatementSetter)创建这些类实例。如果更改工厂,则可以创建模拟

  • 在Spring中,您可以通过构造函数或setter方法注入此依赖项


  • 如果您这样做,您的测试将像一个魔咒。

    谢谢@Bitman,关于如何实施您的建议有什么建议吗?1.调查
    抽象工厂
    工厂方法
    设计模式。2.读一些关于基础弹簧机构的书。写详细的建议太长了。了解
    SOLID
    建模原则,以及
    Clean code