【问题标题】:Spring JUnit and Mockito - SimpleJdbcTemplateSpring JUnit 和 Mockito - SimpleJdbcTemplate
【发布时间】:2011-02-11 20:07:19
【问题描述】:

给定一个扩展 SimpleJdbcDaoSupport 的类,你如何模拟 SimpleJdbcTemplate?

public class SimpleJdbcDaoSupportExtension extends SimpleJdbcDaoSupport {  
     public SimpleJdbcDaoSupportExtension (JdbcTemplate jdbcTemplate){  
             super.setJdbcTemplate(jdbcTemplate);  
     }

     public MyDomainObj getResult(){
         SimpleJdbcTemplate sjdbc = getSimpleJdbcTemplate();  
         MyDomainObj result = sjdbc.query(*whatever necessary args*.);
         return result;
     }
}

然后,使用 Mockito:

public class Test {  
    @Mock private JdbcTemplate mockedJdbcTemplateDedendency;  
    private SimpleJdbcDaoSupportExtension testObj;  

    @Before
    public void doBeforeEachTestCase() {
        MockitoAnnotations.initMocks(this);
        SimpleJdbcDaoSupportExtension sje = new SimpleJdbcDaoSupportExtension (mockedJdbcTemplateDedendency);
    }  
    @Test
    public final void test(){           
        when(mockedJdbcTemplateDedendency.query("what to query").thenReturn(new MyDomainObj());
    }
}

模拟的 JdbcTemplate 被注入,但是由于 dao 类依赖 SimpleJdbcTemplate 进行查询(用于映射到对象),并且它是由 SimpleJdbcDaoSupport 内部构造的 - 模拟 JdcbTemplate 对 SimpleJdbcTemplate 没有影响。那么如何做到这一点,当它没有公共设置器时,并且构造 SimpleJdbcTemplate 的唯一方法是依赖该方法 getSimpleJdbcObject()?

【问题讨论】:

标签: java spring mockito jdbctemplate


【解决方案1】:

你应该模拟一个接口(它有你需要的方法),而不是模拟具体的类。

例如:

public class SimpleJdbcDaoSupportExtension extends SimpleJdbcDaoSupport implements MyDomainDao{  
     public SimpleJdbcDaoSupportExtension (JdbcTemplate jdbcTemplate){  
             super.setJdbcTemplate(jdbcTemplate);  
     }

     public MyDomainObj getResult(){
         SimpleJdbcTemplate sjdbc = getSimpleJdbcTemplate();  
         MyDomainObj result = sjdbc.query(*whatever necessary args*.);
         return result;
     }
}

public class Test {  
    @Mock private MyDomainDao myDomainDao ;
    private YourController yourController;  

    @Before
    public void doBeforeEachTestCase() {
        MockitoAnnotations.initMocks(this);
        yourController = new YourController(myDomainDao);
    }  
    @Test
    public final void testSomething(){           
        when(myDomainDao.getResult().thenReturn(new MyDomainObj());
        //on to testing the usages of myDomainDao
        yourController.doSomething();
        //verify
        verify(myDomainDao, times(2)).getResult();
    }
}

【讨论】:

    【解决方案2】:

    为什么要模拟 JdbcTemplate?将真实的东西与 HSQL 等内存数据库一起使用。

    【讨论】:

    • 我一直在想,如果你使用 HSQL 而不是 mock,你会以某种方式通过某种集成测试来进行真正的单元测试,对吗?
    • @JuanAntonioGomezMoriano 从纯粹主义者的角度来看,是的。我发现单元测试和集成测试之间的界限没有明确定义。我发现最有用的定义(尽管在语义上不正确)是:单元测试是不需要外部服务的任何东西,而集成测试就是其他一切。从这个角度来看,内存数据库可以用于单元测试。
    • 感谢您的解释:)
    • @SeanPatrickFloyd 如果我的代码使用 50 多个表,每个表中有 200 多个列,那该怎么办。您在单元测试中创建整个数据库模式?我不明白这一点..
    【解决方案3】:

    还有一件事。我通过将 SimpleJdbcTemplate 替换为 JdbcTemplate 解决了这个问题。我是 Java 的新手,从 .Net 世界穿越过来,最初使用 SimpleJdbcTemplate 只是因为我遇到了一些描述其用法的文档,这些文档完美地满足了我的需求。但 Skaffman 的 cmets 引导我更深入地研究 JdbcTemplate,然后我意识到我并不真的需要 SimpleJdbcTemplate
    尽管如此,这个问题的哲学部分仍然存在。你如何模拟只能通过询问容器本身来创建的东西?在我看来,Spring 在这里违反了 DI 原则,因为它严格依赖容器。

    【讨论】:

    • 对您的问题的准确回答是,您需要使用一个模拟库,该库支持模拟来自给定基类型的未指定实现类。 JMockit(我开发的一个模拟工具)通过@Capturing注解提供了上述支持,用于声明一个模拟字段/参数。
    【解决方案4】:

    另一种允许您测试更多 dao 的方法是模拟连接、preparedstatement 和结果集。这比仅仅模拟 jdbctemplate 需要更多的工作,但可以让您验证preparedstatement 是否设置了正确的值,以及您的自定义行映射器是否正常工作。

    public class Test {  
      private MyDao dao;  
      private JdbcTemplate jdbcTemplate;
      private Connection conn;
      private PreparedStatement ps;
      private ResultSet rs;
    
    @Before
    public void setUp() throws Exception {
        dao = new MyDao();
        conn = mock(Connection.class);      
        ps = mock(PreparedStatement.class);
        rs = mock(ResultSet.class);
        jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, false));
        doa.setJdbcTemplate(jdbcTemplate);
    }
    
    @Test
    public void test() throws Exception {           
        when(conn.prepareStatement(any(String.class))).thenReturn(ps);
        when(ps.executeQuery()).thenReturn(rs);
        // return one row
        when(rs.next()).thenReturn(true).thenReturn(false);
    
        when(rs.getInt("id")).thenReturn(1234);
        when(rs.getString("name")).thenReturn("Bob");
    
        MyDto myDto = dao.someDaoMethod(...)
    
        // verify ParameterSource
        verify(ps, times(1)).setInt(1, 1234);
    
        // these verify if you are mapping the columns to the right object attribute.
        assertEquals(1234, myDto.getId().intValue());
        assertEquals("Bob", myDto.getName());
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-29
      • 1970-01-01
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      • 2021-11-21
      相关资源
      最近更新 更多