Junit测试后的数据库清理

我必须使用Junit测试一些Thrift服务。 当我将测试作为Thrift客户端运行时,服务会修改服务器数据库。 我无法找到一个好的解决方案,可以在每次测试运行后清理数据库。 清理很重要,特别是因为ID必须是唯一的,目前从XML文件中读取。 现在,我必须在运行测试后手动更改ID,以便下一组测试可以运行而不会在数据库中抛出主键冲突。 如果我可以在每次测试运行后清理数据库,那么问题就完全解决了,否则我将不得不考虑其他解决方案,比如生成随机ID并在需要ID的地方使用它们。

编辑:我想强调一下,我正在测试一个写入数据库的服务,我没有直接访问数据库。 但是,由于服务是我们的,我可以修改服务,以便在需要时提供任何清理方法。

除非您测试特定的数据库操作(例如,validation您可以查询或更新数据库),否则您的JUnits不应该写入真实数据库。 相反,你应该模拟数据库类。 这样,您实际上不必连接和修改数据库,因此不需要进行清理。

您可以通过几种不同的方式模拟您的课程。 您可以使用JMock等库,它将为您执行所有执行和validation工作。 我个人最喜欢的方法是使用dependency injection。 这样我就可以创建实现我的存储库接口的模拟类(你正在使用数据访问层的接口吗?;-))并且我只使用已知的动作/返回值来实现所需的方法。

//Example repository interface. public interface StudentRepository { public List getAllStudents(); } //Example mock database class. public class MockStudentRepository implements StudentRepository { //This method creates fake but known data. public List getAllStudents() { List studentList = new ArrayList(); studentList.add(new Student(...)); studentList.add(new Student(...)); studentList.add(new Student(...)); return studentList; } } //Example method to test. public int computeAverageAge(StudentRepository aRepository) { List students = aRepository.GetAllStudents(); int totalAge = 0; for(Student student : students) { totalAge += student.getAge(); } return totalAge/students.size(); } //Example test method. public void testComputeAverageAge() { int expectedAverage = 25; //What the expected answer of your result set is int actualAverage = computeAverageAge(new MockStudentRepository()); AssertEquals(expectedAverage, actualAverage); } 

如果您使用的是Spring,那么您需要的只是测试类上的@DirtiesContext注释。

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/test-context.xml") @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) public class MyServiceTest { .... } 

如何使用像DBUnit这样的东西 ?

Spring的unit testing框架具有处理JDBC的广泛function。 一般方法是unit testing在事务中运行,并且(在测试之外)一旦测试完成,事务就会回滚。

这样做的好处是能够使用您的数据库及其架构,但不会对数据进行任何直接更改。 当然,如果您在测试中实际执行了提交,那么所有的赌注都会被取消!

有关更多阅读,请查看Spring关于使用JDBC进行集成测试的文档 。

编写JUnit测试时,可以覆盖两个特定的方法:setUp()和tearDown()。 在setUp()中,您可以设置必要的所有内容以便测试代码,这样您就不必在每个特定的测试用例中进行设置。 在所有测试用例运行后调用tearDown()。

如果可能的话,你可以设置它,这样你就可以在setUp()方法中打开你的数据库,然后让它清除测试中的所有内容并在tearDown()方法中关闭它。 这就是我们拥有数据库时所做的所有测试。

下面是一个例子:

 @Override protected void setUp() throws Exception { super.setUp(); db = new WolfToursDbAdapter(mContext); db.open(); //Set up other required state and data } @Override protected void tearDown() throws Exception { super.tearDown(); db.dropTables(); db.close(); db = null; } //Methods to run all the tests 

假设您可以访问数据库:另一个选项是在测试之前创建数据库备份,并在测试之后从该备份恢复。 这可以自动化。

如果您使用的是Spring + Junit 4.x,则无需在DB中插入任何内容。 查看AbstractTransactionalJUnit4SpringContextTests类。

另请参阅Spring文档以获取JUnit支持。

它有点严苛,但我通常的目标是在每次测试方法执行之前消灭数据库(或者只是我感兴趣的表)。 当然,当我进入更多集成类型的测试时,这不会起作用。

在我无法控制数据库的情况下,假设我想validation在给定调用之后创建的行数是否正确,那么测试将计算测试调用之前和之后的行数,并确保差异是正确。 换句话说,考虑现有数据,然后看看测试代码如何改变事物,而不假设现有数据。 设置可能需要一些工作,但让我来测试更“实时”的系统。

在您的情况下,具体ID是否重要? 你可以动态生成ID,也许随机生成,validation它们还没有被使用,然后继续?

如果您正在尝试测试从数据库中提取的数据,我同意Brainimus。 如果您希望测试对数据库所做的修改,另一种解决方案是模拟数据库本身。 您可以使用多种内存数据库实现来创建临时数据库(例如在JUnit的setUp()期间),然后从内存中删除整个数据库(在tearDown()期间)。 只要您没有使用特定于供应商的SQL,那么这是一种测试修改数据库而不涉及真实生产数据库的好方法。

一些提供内存支持的优秀Java数据库是Apache Derby , Java DB (但它实际上是Oracle的Apache Derby风格), HyperSQL (更好地称为HSQLDB)和H2数据库引擎 。 我个人使用HSQLDB来创建内存模拟数据库进行测试,它运行良好,但我相信其他人会提供类似的结果。