分享

Spring+hibernate+Junit+dbunit的整合和常见问题整合

 蹇胜雄 2015-09-23

dbunit是一个用来进行数据库测试的框架,使用dbunit可以有效的进行数据库的初始化操作,方便开发人员进行数据库的备份,测试和恢复,目前Spring提供比较好的测试整合,可以通过Annotation非常方便的和junit整合,该文主要讲解一些个人对Spring,Junit和dbunit三者整合的心得和一些问题, 主要分成以下几部分介绍。

一、Spring和Junit

Spring和Junit整合非常容易,在Spring3之后就提供了一些非常方便的Annotation来完成对要测试对象的依赖注入

@RunWith(SpringJUnit4ClassRunner.class)//让junit工作在spring环境中

@ContextConfiguration("/beans.xml")//在classes中spring的配置文件

public class TestUser {

   

    @Inject

    private IUserDao userDao;

 

}

以上基本上完成了Spring和junit的整合,但是无法开启事务,需要通过@TransactionConfiguration和@Transactional两个Anontation来进行指定

@RunWith(SpringJUnit4ClassRunner.class)//让junit工作在spring环境中

@ContextConfiguration("/beans.xml")//在classes中spring的配置文件

//transactionManager表示在spring配置文件中所声明的事务对象

//defaultRollback=true表示操作会自动回滚,这样你在单元测试中所作的操作都不会影响数据库中的数据

@TransactionConfiguration(transactionManager="txManager", defaultRollback=true)

@Transactional

public class TestUser {

   

    @Inject

    private IUserDao userDao;

                                                          

    @Test

    public void testLoad() {

       

        User u = userDao.load(1);

        u.setNickname("ddd");

        //此时可以完成更新,但是当测试结束不会影响数据库

        userDao.update(u);

        System.out.println(u.getNickname());

    }

 

}

二、dbunit的使用

dbunit可以自动导入配置文件中的数据,并且可以把数据库中的表生成相应的xml配置文件。

2.1、使用dbunit首先创建一个基本的配置文件在classpath中,以下是user.xml文件内容

 

   

        nickname="admin1" email="admin1@admin.com" status="1" phone="110"

        create_date="2010-12-12"/>

   

        nickname="admin1" email="admin1@admin.com" status="1" phone="110"

        create_date="2010-12-12"/>

   

        nickname="admin1" email="admin1@admin.com" status="1" phone="110"

        create_date="2010-12-12"/>

 

2.2、创建IDatabaseConnection、IDataSet并且完成测试

IDatabaseConnection是dbunit中重要的部分,等于jdbc的Connection,通过IDatabaseConnection可以创建dbunit所需要的记录集等对象,IDatabaseConnection的创建需要基于JDBC的Connection,以下是创建IDatabaseConnection的代码,Dbutil中可以通过原始的jdbc的方式创建Connection也可以通过Spring中配置的SessionFactory来获取Connection

1
dbunitCon new DatabaseConnection(DbUtil.getConnection());

可以通过该对象创建相应的IDataSet对象,以上xml中的格式是通过FlatXmlDataSet对象进行解析,所以通过该对象可以进行记录集的初始化等操作

 

    protected IDataSet createDateSet(String tname) throws DataSetException {

        //配置文件在classes的根目录下

        InputStream is = AbstractDbUnitTestCase

                    .class

                    .getClassLoader().getResourceAsStream(tname+".xml");

        Assert.assertNotNull("dbunit的基本数据文件不存在",is);

        //创建DataSet

        return new FlatXmlDataSet(new FlatXmlProducer(new InputSource(is)));

 

    }

以下代码是进行测试的代码

    @Test

public void testListUserRoleIds() throws Exception {

    //创建DataSet对象

    IDataSet ds = createDateSet("user");

    //将配置文件的数据插入到数据库中

    DatabaseOperation.CLEAN_INSERT.execute(dbunitCon,ds);

    //进行验证

    List actuals = Arrays.asList(2,3);

    List expected = userDao.listUserRoleIds(2);

    EntitiesHelper.assertObjects(expected, actuals);

 

}

三、解决dbunit可能存在的问题

3.1、对象的外键自关联问题,当存在某个对象和自己有关联的时候,使用dbunit可能存在如下问题,第一外键初始化问题

   

   

   

   

   

 

   

对于dbunit而言读取配置文件,会以第一个节点作为插入数据的基础,此时由于第一个节点没有pid所以,在数据库的初始化的时候对于其他节点就不会将pid初始化到数据库中,所以无法满足要求,解决这个问题的最好方法是为这个节点设置相应的DTD文件,通过DTD文件来指定这个表的属性,DTD的文件可以通过-->FlatDtdDataSet.write(ds, new FileWriter(dtdFile));写到指定的文件中,并且在创建IDataSet时通过

1
new FlatXmlDataSet(new FlatXmlProducer(new InputSource(is),new FlatDtdDataSet(new FileReader(dtdFile))));

引入相应的DTD文件,这样就可以解决该问题。

3.2、在自关联进行CLEAN_INSERT时报com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException错

这个是由于删除时有外键约束规则的限制,无法删除,此时只要在链接字符串中设置不进行外键约束即可

1
2
3
4
5
6
7
public static Connection getConnection() throws SQLException {
        Connection con null;
        //sessionVariables=FOREIGN_KEY_CHECKS=0表示忽略外键关联
        con DriverManager.getConnection("jdbc:mysql://localhost:3306/fz_cms?sessionVariables=FOREIGN_KEY_CHECKS=0"
                "fz""fz123");
        return con;
    }


3.3、数据的还原问题,可能在进行数据库测试时希望测试数据不会影响到原有数据,可以考虑在进行测试之前把数据库先进行备份,之后在测试之后进行还原。为了让测试可以复用,可以考虑写一个公共的测试类来完成dbunit的初始化操作,以下是这个类完整代码
public class AbstractDbUnitTestCase {
    public static IDatabaseConnection dbunitCon;
    //临时文件,用来存储数据库的备份数据
    private File tempFile;
    //临时文件,用来存储数据库的dtd文件
    private File dtdFile;
                                     
    @BeforeClass
    public static void init() throws DatabaseUnitException, SQLException {
        //初始化IDatabaseConnection
        dbunitCon = new DatabaseConnection(DbUtil.getConnection());
    }
                                     
    //根据文件名称创建DataSet对象
    protected IDataSet createDateSet(String tname) throws DataSetException, FileNotFoundException, IOException {
        InputStream is = AbstractDbUnitTestCase
                    .class
                    .getClassLoader().getResourceAsStream(tname+".xml");
        Assert.assertNotNull("dbunit的基本数据文件不存在",is);
        //通过dtd和传入的文件创建测试的IDataSet
        return new FlatXmlDataSet(new FlatXmlProducer(new InputSource(is),new FlatDtdDataSet(new FileReader(dtdFile))));
    }
                                     
    //备份数据库的所有表
    protected void backupAllTable() throws SQLException, IOException, DataSetException {
        IDataSet ds = dbunitCon.createDataSet();
        writeBackupFile(ds);
    }
                                     
    //将表写到临时文件中
    private void writeBackupFile(IDataSet ds) throws IOException, DataSetException {
        tempFile = File.createTempFile("back", "xml");
        dtdFile = File.createTempFile("back","dtd");
        //写dtd
        FlatDtdDataSet.write(ds, new FileWriter(dtdFile));
        //写数据表中的文件
        FlatXmlDataSet.write(ds, new FileWriter(tempFile));
    }
    //备份指定表
    protected void backupCustomTable(String[] tname) throws DataSetException, IOException {
        QueryDataSet ds = new QueryDataSet(dbunitCon);
        for(String str:tname) {
            ds.addTable(str);
        }
        writeBackupFile(ds);
    }
    //备份一张表
    protected void bakcupOneTable(String tname) throws DataSetException, IOException {
        backupCustomTable(new String[]{tname});
    }
    //还原备份的表
    protected void resumeTable() throws DatabaseUnitException, SQLException, IOException {
        //创建ds的时候引入相应的dtd
        IDataSet ds = new FlatXmlDataSet(new FlatXmlProducer(
                    new InputSource(new FileInputStream(tempFile)),new FlatDtdDataSet(new FileReader(dtdFile))));
        DatabaseOperation.CLEAN_INSERT.execute(dbunitCon, ds);
    }
                                     
    //清空数据
    @AfterClass
    public static void destory() {
        try {
            if(dbunitCon!=null) dbunitCon.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

此时只要将测试类继承于这个类就拥有了相应的方法了

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多