zoukankan      html  css  js  c++  java
  • springmock + dbutil 用来测试数据库操作

    大概流程:
    1.用dbunit创建初始的测试数据。
    2.用spring-mock 维护测试过程中的数据会滚,这样可以保证测试后数据库保持原状态。
    3.用junit架构测试。
    4.用dbunit销毁初始测试数据。

    http://www.iteye.com/topic/475172

    Java代码 复制代码 收藏代码
    1. package com.test.dbunit.dao;   
    2.   
    3. import javax.sql.DataSource;   
    4.   
    5. import org.dbunit.Assertion;   
    6. import org.dbunit.database.DatabaseConnection;   
    7. import org.dbunit.database.IDatabaseConnection;   
    8. import org.dbunit.database.QueryDataSet;   
    9. import org.dbunit.dataset.IDataSet;   
    10. import org.dbunit.dataset.xml.FlatXmlDataSet;   
    11. import org.dbunit.operation.DatabaseOperation;   
    12. import org.junit.Assert;   
    13. import org.junit.Before;   
    14. import org.junit.Test;   
    15. import org.springframework.beans.factory.annotation.Autowired;   
    16. import org.springframework.core.io.ClassPathResource;   
    17. import org.springframework.jdbc.datasource.DataSourceUtils;   
    18. import org.springframework.test.context.ContextConfiguration;   
    19. import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;   
    20. import org.springframework.test.context.transaction.TransactionConfiguration;   
    21.   
    22. import com.test.dbunit.entity.User;   
    23.   
    24.   
    25. @ContextConfiguration(locations = { "classpath:testApplicationContext.xml" })   
    26. @TransactionConfiguration(defaultRollback = true)   
    27. public class UserDaoTest extends AbstractTransactionalJUnit4SpringContextTests {   
    28.     @Autowired  
    29.     private UserDao userDao;   
    30.   
    31.     @Autowired  
    32.     private DataSource dataSource;   
    33.   
    34.     private IDatabaseConnection conn;   
    35.   
    36.     @Before  
    37.     public void initDbunit() throws Exception {   
    38.         conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));   
    39.     }   
    40.   
    41.     @Test  
    42.     public void saveUser() throws Exception {   
    43.         User user = new User();   
    44.         user.setNick("user001");   
    45.         user.setPassword("password001");   
    46.         userDao.save(user);   
    47.   
    48.         QueryDataSet actual = new QueryDataSet(conn);   
    49.         actual.addTable("user",   
    50.                 "select * from user where user.nick = 'user001'");   
    51.   
    52.         IDataSet expected = new FlatXmlDataSet(new ClassPathResource(   
    53.                 "com/taobao/dbunit/dao/user001.xml").getFile());   
    54.   
    55.         Assertion.assertEquals(expected, actual);   
    56.     }   
    57.   
    58.     @Test  
    59.     public void updateUser() throws Exception {   
    60.   
    61.         IDataSet origen = new FlatXmlDataSet(new ClassPathResource(   
    62.                 "com/taobao/dbunit/dao/user001.xml").getFile());   
    63.   
    64.         DatabaseOperation.INSERT.execute(conn, origen);   
    65.   
    66.         User user = new User();   
    67.         user.setNick("user001");   
    68.         user.setPassword("password002");   
    69.         userDao.update(user);   
    70.   
    71.         QueryDataSet actual = new QueryDataSet(conn);   
    72.         actual.addTable("user",   
    73.                 "select * from user where user.nick = 'user001'");   
    74.   
    75.         IDataSet expected = new FlatXmlDataSet(new ClassPathResource(   
    76.                 "com/taobao/dbunit/dao/user001_updated.xml").getFile());   
    77.   
    78.         Assertion.assertEquals(expected, actual);   
    79.     }   
    80.   
    81.     @Test  
    82.     public void removeUser() throws Exception {   
    83.         IDataSet origen = new FlatXmlDataSet(new ClassPathResource(   
    84.                 "com/taobao/dbunit/dao/user001.xml").getFile());   
    85.         DatabaseOperation.INSERT.execute(conn, origen);   
    86.   
    87.         userDao.remove("user001");   
    88.   
    89.         QueryDataSet actual = new QueryDataSet(conn);   
    90.         actual.addTable("user""select * from user where nick = 'user001'");   
    91.   
    92.         Assert.assertEquals(0, actual.getTable("user").getRowCount());   
    93.            
    94.     }   
    95.        
    96.     @Test  
    97.     public void findUser() throws Exception {   
    98.         IDataSet data = new FlatXmlDataSet(new ClassPathResource(   
    99.                 "com/taobao/dbunit/dao/user001.xml").getFile());   
    100.         DatabaseOperation.INSERT.execute(conn, data);   
    101.   
    102.            
    103.         User user = userDao.getUserByNick("user001");   
    104.   
    105.         Assert.assertEquals("password001", user.getPassword());   
    106.     }   
    107.   
    108. }  

     对Dao进行单元测试,一般有两种思路。一是Mock,对使用的底层API进行Mock,比如Hibernate和JDBC接口,判断接口有没有正确调用,另一种是实际访问数据库,判断数据库有没有正确读写。更多的情况下,我更倾向于后者,因为在使用ORM工具或者jdbcTemplate的情况下,dao一般只有简单的几行代码,没有复杂的逻辑,Mock测试一般没有什么意义,我们更关心的是,Hibernate mapping是否正确,ibatis sql是否正确等,所以实际读写数据库才能真正判断一个dao是否正确,这也是我们关心的测试内容。

    好的单元测试应该是原子性的,独立的,不应依赖其他测试和上下文,但是要测试数据读写是否正确,就必须涉及初始数据的加载,数据修改的还原等操作。对于初始数据的加载,手动输入很麻烦,一个解决方案就是使用Dbunit,从Xml文件甚至Excel中加载初始数据到数据库,是数据库的值达到一个已知状态。同时还可以使用Dbunit,对数据库的结果状态进行判断,保证和期望的一致。数据修改的还原,可以依赖Spring TransactionalTests,在测试完成后回滚数据库。

    Dbunit还可以对数据的现有数据进行备份,还原,清空现有数据,一个好的测试实践是每一个开发人员一个测试数据库,进而对数据库的数据状态有更好的控制,但现实可能会是共享同一个测试库,所以这种情况下,测试的编写必须多做一些考虑。

    待测试的类:

    Java代码 复制代码 收藏代码
    1. package com.test.dbunit.dao.impl;   
    2.   
    3. import java.sql.ResultSet;   
    4. import java.sql.SQLException;   
    5.   
    6. import org.springframework.jdbc.core.RowMapper;   
    7.   
    8. import com.test.dbunit.dao.UserDao;   
    9. import com.test.dbunit.entity.User;   
    10.   
    11. public class DefaultUserDao extends BaseDao implements UserDao {   
    12.   
    13.     private static String QUERY_BY_NICK = "select * from user where user.nick = ?";   
    14.        
    15.     private static String REMOVE_USER = "delete from user where user.nick = ?";   
    16.        
    17.     private static String INSERT_USER = "insert into user(nick,password) values(?, ?)";   
    18.        
    19.     private static String UPDATE_USER = "update user set user.password = ? where user.nick = ?";   
    20.     @Override  
    21.     public User getUserByNick(String nick) {   
    22.         return (User) getJdbcTemplate().queryForObject(QUERY_BY_NICK,new Object[]{nick}, new RowMapper(){   
    23.             @Override  
    24.             public Object mapRow(ResultSet rs, int index) throws SQLException {   
    25.                 User user = new User();   
    26.                 user.setNick(rs.getString("nick"));   
    27.                 user.setPassword(rs.getString("password"));   
    28.                 return user;   
    29.             }   
    30.         });   
    31.     }   
    32.   
    33.     @Override  
    34.     public void remove(String nick) {   
    35.         getJdbcTemplate().update(REMOVE_USER, new Object[]{nick});   
    36.     }   
    37.   
    38.     @Override  
    39.     public void save(User user) {   
    40.         getJdbcTemplate().update(INSERT_USER, new Object[]{user.getNick(), user.getPassword()});   
    41.     }   
    42.   
    43.     @Override  
    44.     public void update(User user) {   
    45.         getJdbcTemplate().update(UPDATE_USER, new Object[]{user.getPassword(), user.getNick()});   
    46.     }   
    47.   
    48. }  

     单元测试:

    需要注意的地方就是,DataSourceUtils.getConnection(datasource) 通过这种方式获得数据库连接初始化Dbunit,能够保证Dbunit使用的数据连接和当前事务的数据库连接相同,保证能够在参与到事务中。Spring的TransactionManager会在开始事务时把当前连接保存到ThreadLocal中,DataSourceUtils.getConnection方法,首先从ThreadLocal中获取连接。 

    user001.xml

    Xml代码 复制代码 收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <dataset>  
    3.     <user nick="user001" password="password001" />  
    4. </dataset>  

     使用dbunit,可以通过xml文件定义数据集,也可以使用其他方式定义,比如Excel,编程方式。

    Dbunit的主要构件

    IDatabaseConnection

    数据库链接。实现类有DatabaseConnectionDatabaseDataSourceConnection ,执行数据库操作时需要一个连接。

    IDataSet

    数据集,数据集可以从Xml文件Excel等外部文件获取,也可以从数据库查询,或者编程方式构件,数据集可以作为初始数据插入到数据库,也可以作为断言的依据。另外还有IDatatable等辅助类。

    比如在updateUser测试中,使用了QueryDataSet,从数据库中构建一个Dataset,再通过FlatXmlDataSet从Xml文件中构建一个Dataset,断言这两个Dataset相同。

    Java代码 复制代码 收藏代码
    1. QueryDataSet actual = new QueryDataSet(conn);   
    2. actual.addTable("user""select * from user where user.nick = 'user001'");   
    3.   
    4. IDataSet expected = new FlatXmlDataSet(new ClassPathResource(   
    5.     "com/taobao/dbunit/dao/user001_updated.xml").getFile());   
    6.   
    7. Assertion.assertEquals(expected, actual);  

    DatabaseOperation 

    通过定义的静态字段可以获取一组代表一个数据操作的子类对象,比如DatabaseOperation .INSERT,返回 InsertOperation,通过执行execute方法把数据集插入到数据库。例如:

    Java代码 复制代码 收藏代码
    1. IDataSet origen = new FlatXmlDataSet(new ClassPathResource(   
    2.                 "com/taobao/dbunit/dao/user001.xml").getFile());   
    3. DatabaseOperation.INSERT.execute(conn, origen);  

     从Xml文件中构建DataSet,使用Insert插入到数据库,初始化测试数据。

    Assertion

    唯一的方法,assertEqual,断言两个数据集或数据表相同。

    更多关于Dbunit的组件的介绍:http://www.dbunit.org/components.html

    PS:使用Oracle的时候,初始化DatabaseConnection需要传入scheme。new DatabaseConnection(conn,SCHEMA_NAME ) ,SCHMEA_NAME需要大写。

     附件提供所有代码下载

    一个DAO测试基类

    Java代码 复制代码 收藏代码
    1. package com.taobao.dbunit.dao;   
    2.   
    3. import java.sql.SQLException;   
    4.   
    5. import javax.sql.DataSource;   
    6.   
    7. import org.dbunit.Assertion;   
    8. import org.dbunit.database.DatabaseConnection;   
    9. import org.dbunit.database.IDatabaseConnection;   
    10. import org.dbunit.dataset.DataSetException;   
    11. import org.dbunit.dataset.DefaultDataSet;   
    12. import org.dbunit.dataset.DefaultTable;   
    13. import org.dbunit.dataset.IDataSet;   
    14. import org.dbunit.dataset.xml.FlatXmlDataSet;   
    15. import org.dbunit.operation.DatabaseOperation;   
    16. import org.junit.Assert;   
    17. import org.junit.Before;   
    18. import org.springframework.beans.factory.annotation.Autowired;   
    19. import org.springframework.core.io.ClassPathResource;   
    20. import org.springframework.jdbc.datasource.DataSourceUtils;   
    21. import org.springframework.test.context.ContextConfiguration;   
    22. import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;   
    23. import org.springframework.test.context.transaction.TransactionConfiguration;   
    24.   
    25. @ContextConfiguration(locations = { "classpath:testApplicationContext.xml" })   
    26. @TransactionConfiguration(defaultRollback = true)   
    27. public class BaseDaoTest extends AbstractTransactionalJUnit4SpringContextTests {   
    28.   
    29.     @Autowired  
    30.     private DataSource dataSource;   
    31.   
    32.     private IDatabaseConnection conn;   
    33.   
    34.     @Before  
    35.     public void initDbunit() throws Exception {   
    36.         conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));   
    37.     }   
    38.   
    39.     /**  
    40.      * 清空file中包含的表中的数据,并插入file中指定的数据  
    41.      *   
    42.      * @param file  
    43.      * @throws Exception  
    44.      */  
    45.     protected void setUpDataSet(String file) throws Exception {   
    46.         IDataSet dataset = new FlatXmlDataSet(new ClassPathResource(file)   
    47.                 .getFile());   
    48.         DatabaseOperation.CLEAN_INSERT.execute(conn, dataset);   
    49.     }   
    50.   
    51.     /**  
    52.      * 验证file中包含的表中的数据和数据库中的相应表的数据是否一致  
    53.      *   
    54.      * @param file  
    55.      * @throws Exception  
    56.      */  
    57.     protected void verifyDataSet(String file) throws Exception {   
    58.         IDataSet expected = new FlatXmlDataSet(new ClassPathResource(file)   
    59.                 .getFile());   
    60.         IDataSet dataset = conn.createDataSet();   
    61.   
    62.         for (String tableName : expected.getTableNames()) {   
    63.             Assertion.assertEquals(expected.getTable(tableName), dataset   
    64.                     .getTable(tableName));   
    65.         }   
    66.   
    67.     }   
    68.   
    69.     /**  
    70.      * 清空指定的表中的数据  
    71.      *   
    72.      * @param tableName  
    73.      * @throws Exception  
    74.      */  
    75.     protected void clearTable(String tableName) throws Exception {   
    76.         DefaultDataSet dataset = new DefaultDataSet();   
    77.         dataset.addTable(new DefaultTable(tableName));   
    78.         DatabaseOperation.DELETE_ALL.execute(conn, dataset);   
    79.     }   
    80.   
    81.     /**  
    82.      * 验证指定的表为空  
    83.      *   
    84.      * @param tableName  
    85.      * @throws DataSetException  
    86.      * @throws SQLException  
    87.      */  
    88.     protected void verifyEmpty(String tableName) throws DataSetException,   
    89.             SQLException {   
    90.         Assert.assertEquals(0, conn.createDataSet().getTable(tableName)   
    91.                 .getRowCount());   
    92.     }   
    93. }  

     使用:

    Java代码 复制代码 收藏代码
    1. @Test  
    2. public void updateUser() throws Exception {   
    3.     setUpDataSet("com/taobao/dbunit/dao/user001.xml");   
    4.        
    5.     User user = new User();   
    6.     user.setNick("user001");   
    7.     user.setPassword("password002");   
    8.     userDao.update(user);   
    9.   
    10.     verifyDataSet("com/taobao/dbunit/dao/user001_updated.xml");   
    11. }  

      http://yuan.iteye.com/blog/470418

    http://www.iteye.com/topic/21630?page=2#181380

    http://www.iteye.com/topic/21630?page=2#181380

    http://www.blogjava.net/aoxj/archive/2010/08/26/329975.html

  • 相关阅读:
    【2018 “百度之星”程序设计大赛
    分班级(经典二分)
    【2018 “百度之星”程序设计大赛
    【zznu-夏季队内积分赛3-J】追忆
    常见网络名词解释
    【zznu-夏季队内积分赛3-G】2333
    【zznu-夏季队内积分赛3-F】学无止境
    【zznu-夏季队内积分赛3-I】逛超市
    html/css/javascript练习代码
    花生壳免费域名80端口无法访问问题处理
  • 原文地址:https://www.cnblogs.com/opaljc/p/2396441.html
Copyright © 2011-2022 走看看