DBUnit 不会在每个方法之后清理和插入数据库,因此测试不是独立的

发布于 2024-12-08 10:44:18 字数 1194 浏览 0 评论 0原文

我对 DAO 类进行了测试,我使用 DBUnit 创建并填充数据库(使用内存德比)。 我在测试 dao update 方法时遇到问题,因为它修改了数据,然后其他测试失败。我们都知道测试应该独立于其他测试,而且我知道 DBUnit 有一些工具可以在每次测试后清理和重新生成数据库。 但这不起作用!

代码是这样的(TestNG):

@BeforeMethod
public void prepareData() throws Exception {
  cleanAndPopulate("users");
}

public void cleanAndPopulate (String nameXML) throws Exception {
  IDatabaseConnection conn; 
  conn = new DatabaseConnection (sessionForTesting.connection());        
  InputStream is = DBconnection.class.getClassLoader()
    .getResourceAsStream(nameXML + ".xml");      
  dataset = new FlatXmlDataSet(is);
  System.out.println("*** Preparando base de datos de test"); 
  DatabaseOperation.CLEAN_INSERT.execute(conn, dataset); 
}

这是测试(禁用以避免附带影响):

@Test(enabled=false) // Deja la BBDD en estado erroneo!!!
public void busco_y_actualizo() throws Exception { 
    PacoUser resultado = userdao.getById(1L);
    resultado.setName("OTRO");
    userdao.update(resultado);
    PacoUser resultado2 = userdao.getById(1L);
    AssertJUnit.assertNotNull(resultado2); 
    AssertJUnit.assertEquals("OTRO", resultado2.getName());    
}

I have a test for a DAO class, I use DBUnit to create and populate the database (using an in-memory derby).
I am in problems when testing the dao update method because it modify the data and then the other test fails. As all of us know a test should be independent of any other, and I know that DBUnit has some facilities to clean and regenerate the database after every test.
But it does not work!

The code is this (TestNG):

@BeforeMethod
public void prepareData() throws Exception {
  cleanAndPopulate("users");
}

public void cleanAndPopulate (String nameXML) throws Exception {
  IDatabaseConnection conn; 
  conn = new DatabaseConnection (sessionForTesting.connection());        
  InputStream is = DBconnection.class.getClassLoader()
    .getResourceAsStream(nameXML + ".xml");      
  dataset = new FlatXmlDataSet(is);
  System.out.println("*** Preparando base de datos de test"); 
  DatabaseOperation.CLEAN_INSERT.execute(conn, dataset); 
}

This is the test (disabled to avoid collateral effects):

@Test(enabled=false) // Deja la BBDD en estado erroneo!!!
public void busco_y_actualizo() throws Exception { 
    PacoUser resultado = userdao.getById(1L);
    resultado.setName("OTRO");
    userdao.update(resultado);
    PacoUser resultado2 = userdao.getById(1L);
    AssertJUnit.assertNotNull(resultado2); 
    AssertJUnit.assertEquals("OTRO", resultado2.getName());    
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

白云悠悠 2024-12-15 10:44:18

这是因为 CLEAN_INSERT 在测试之前而不是测试之后执行“CLEAN”。

例如,如果有两个测试,test1 和 test2。 test1 和 test2 分别填充 test1.xml 和 test2.xml 中的表。

test1.xml 就像

<dataset>
  <table1 ... />
  <table2 ... />
</dataset>

test2.xml 就像

<dataset>
  <table1 ... />
</dataset>

当测试的顺序是 test1 然后是 test2 时,CLEAN_INSERT 将执行以下操作:

  1. 从 table2 中删除所有内容
  2. 从 table1 中删除所有
  3. 内容 将 test1.xml 中的数据插入 table1
  4. 将 test1.xml 中的数据插入into table2
  5. 执行 test1
  6. 从 table1 删除所有
  7. 内容 将 test2.xml 中的数据插入 table1
  8. 执行 test2

因此,当执行 test2 时,table1 具有 test2.xml 中的数据,这正是我们所期望的。但table2仍然包含test1的数据,这可能会导致一些问题。

解决方法是为所有 xml 文件中的每个表设置一个空行。它将确保在插入之前清理所有表。

对于上面的例子,

test1.xml将像

<dataset>
  <table1 ... />
  <table2 ... />
  <table1 />
  <table2 />
</dataset>

test2.xml一样

<dataset>
  <table1 ... />
  <table1 />
  <table2 />
</dataset> 

It is because CLEAN_INSERT does "CLEAN" before the test, not after the test.

For example, if there are two tests, test1 and test2. test1 and test2 populate tables from test1.xml and test2.xml respectively.

test1.xml is like

<dataset>
  <table1 ... />
  <table2 ... />
</dataset>

test2.xml is like

<dataset>
  <table1 ... />
</dataset>

When the order of tests is test1 and then test2, CLEAN_INSERT will do the following operations:

  1. delete all from table2
  2. delete all from table1
  3. insert data from test1.xml into table1
  4. insert data from test1.xml into table2
  5. execute test1
  6. delete all from table1
  7. insert data from test2.xml into table1
  8. execute test2

So when test2 is executed, table1 has data from test2.xml, which is what we expect. But table2 still contains data for test1, which may cause some issues.

A workaround is to have an empty row for each table in all xml files. It will make sure all tables would be cleaned before insert.

For the above example,

test1.xml will be like

<dataset>
  <table1 ... />
  <table2 ... />
  <table1 />
  <table2 />
</dataset>

test2.xml is like

<dataset>
  <table1 ... />
  <table1 />
  <table2 />
</dataset> 
毁虫ゝ 2024-12-15 10:44:18
@After
public void after() throws SQLException {
    Session session = hibernateSessionFactory.openSession();
    try {
        Work work = new Work() {

            @Override
            public void execute(Connection connection) throws SQLException {
                IDatabaseConnection dbConn = null;
                try {
                    dbConn = getConnection(connection);
                } catch (DatabaseUnitException e) {
                    logger.error("Exception in before", e);
                    Assert.fail(e.getMessage());
                }

                try {
                    List<String> resultList = (List<String>) hibernateTemplate
                            .execute(new HibernateCallback() {
                                String sql = "SELECT st.TABLE_NAME FROM INFORMATION_SCHEMA.SYSTEM_TABLES st where st. TABLE_TYPE='TABLE'";

                                public List<String> doInHibernate(
                                        Session session)
                                        throws HibernateException,
                                        SQLException {
                                    Query query = session
                                            .createSQLQuery(sql);
                                    List<String> list = query.list();
                                    return list;
                                }
                            });

                    QueryDataSet partialDataSet = new QueryDataSet(dbConn);
                    for (String tableName : resultList) {
                        partialDataSet.addTable(tableName);
                    }

                    DatabaseOperation.DELETE_ALL.execute(dbConn,
                            partialDataSet);

                } catch (Exception e) {
                    logger.error("Exception in after", e);
                    Assert.fail(e.getMessage());
                } finally {
                    dbConn.close();
                }

            }

        };
        session.doWork(work);

    } catch (Exception e) {
        logger.error("Exception in after", e);
        Assert.fail(e.getMessage());
    } finally {
        session.close();
    }
}

protected DatabaseConnection getConnection(Connection connection)
        throws DatabaseUnitException, SQLException {
    return new DatabaseConnection(connection, SCHEMA);
}
@After
public void after() throws SQLException {
    Session session = hibernateSessionFactory.openSession();
    try {
        Work work = new Work() {

            @Override
            public void execute(Connection connection) throws SQLException {
                IDatabaseConnection dbConn = null;
                try {
                    dbConn = getConnection(connection);
                } catch (DatabaseUnitException e) {
                    logger.error("Exception in before", e);
                    Assert.fail(e.getMessage());
                }

                try {
                    List<String> resultList = (List<String>) hibernateTemplate
                            .execute(new HibernateCallback() {
                                String sql = "SELECT st.TABLE_NAME FROM INFORMATION_SCHEMA.SYSTEM_TABLES st where st. TABLE_TYPE='TABLE'";

                                public List<String> doInHibernate(
                                        Session session)
                                        throws HibernateException,
                                        SQLException {
                                    Query query = session
                                            .createSQLQuery(sql);
                                    List<String> list = query.list();
                                    return list;
                                }
                            });

                    QueryDataSet partialDataSet = new QueryDataSet(dbConn);
                    for (String tableName : resultList) {
                        partialDataSet.addTable(tableName);
                    }

                    DatabaseOperation.DELETE_ALL.execute(dbConn,
                            partialDataSet);

                } catch (Exception e) {
                    logger.error("Exception in after", e);
                    Assert.fail(e.getMessage());
                } finally {
                    dbConn.close();
                }

            }

        };
        session.doWork(work);

    } catch (Exception e) {
        logger.error("Exception in after", e);
        Assert.fail(e.getMessage());
    } finally {
        session.close();
    }
}

protected DatabaseConnection getConnection(Connection connection)
        throws DatabaseUnitException, SQLException {
    return new DatabaseConnection(connection, SCHEMA);
}
厌味 2024-12-15 10:44:18

确保在每个测试用例之前重置数据库以确保测试独立性。 @BeforeMethod 仅在所有测试用例运行之前调用一次,因此将 cleanAndPopulate 放在这里是不够的。

Make sure that the database is reset before every single test case to ensure test independency. @BeforeMethod is only called once before all test cases are run, so putting cleanAndPopulate here is not enough.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文