Java Junit 测试后的数据库清理

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3412575/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 23:04:31  来源:igfitidea点击:

Database cleanup after Junit tests

javadatabaseunit-testingjunit

提问by Ashish

I have to test some Thrift services using Junit. When I run my tests as a Thrift client, the services modify the server database. I am unable to find a good solution which can clean up the database after each test is run. Cleanup is important especially because the IDs need to be unique which are currently read form an XML file. Now, I have to manually change the IDs after running tests, so that the next set of tests can run without throwing primary key violation in the database. If I can cleanup the database after each test run, then the problem is completely resolved, else I will have to think about other solutions like generating random IDs and using them wherever IDs are required.

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

Edit: I would like to emphasize that I am testing a service, which is writing to database, I don't have direct access to the database. But since, the service is ours, I can modify the service to provide any cleanup method if required.

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

采纳答案by brainimus

Unless you as testing specific database actions (verifying you can query or update the database for example) your JUnits shouldn't be writing to a real database. Instead you should mock the database classes. This way you don't actually have to connect and modify the database and therefor no cleanup is needed.

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

You can mock your classes a couple of different ways. You can use a library such as JMockwhich will do all the execution and validation work for you. My personal favorite way to do this is with Dependency Injection. This way I can create mock classes that implement my repository interfaces (you are using interfaces for your data access layer right? ;-)) and I implement only the needed methods with known actions/return values.

您可以通过几种不同的方式模拟您的课程。您可以使用JMock 之类的库,它将为您完成所有执行和验证工作。我个人最喜欢的方法是使用依赖注入。通过这种方式,我可以创建实现我的存储库接口的模拟类(您正在为您的数据访问层使用接口,对吗?;-))并且我只实现具有已知操作/返回值的所需方法。

//Example repository interface.
public interface StudentRepository
{
   public List<Student> getAllStudents();
}

//Example mock database class.
public class MockStudentRepository implements StudentRepository
{
   //This method creates fake but known data.
   public List<Student> getAllStudents()
   {
      List<Student> studentList =  new ArrayList<Student>();
      studentList.add(new Student(...));
      studentList.add(new Student(...));
      studentList.add(new Student(...));

      return studentList;
   }
}

//Example method to test.
public int computeAverageAge(StudentRepository aRepository)
{
   List<Student> 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);
}

回答by Manfred

Assuming you have access to the database: Another option is to create a backup of the database just before the tests and restore from that backup after the tests. This can be automated.

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

回答by Sheetal Mohan Sharma

If you are using Spring + Junit 4.x then you don't need to insert anything in DB. Look at AbstractTransactionalJUnit4SpringContextTestsclass.

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

Also check out the Spring documentation for JUnit support.

另请查看有关 JUnit 支持的 Spring 文档。

回答by Noel M

Spring's unit testing framework has extensive capabilities for dealing with JDBC. The general approach is that the unit tests runs in a transaction, and (outside of your test) the transaction is rolled back once the test is complete.

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

This has the advantage of being able to use your database and its schema, but without making any direct changes to the data. Of course, if you actually perform a commit inside your test, then all bets are off!

这具有能够使用您的数据库及其架构的优势,但无需对数据进行任何直接更改。当然,如果您确实在测试中执行了提交,那么所有赌注都将取消!

For more reading, look at Spring's documentationon integration testing with JDBC.

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

回答by Rodney Gitzel

It's a bit draconian, but I usually aim to wipe out the database (or just the tables I'm interested in) before every test method execution. This doesn't tend to work as I move into more integration-type tests of course.

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

In cases where I have no control over the database, say I want to verify the correct number of rows were created after a given call, then the test will count the number of rows before and after the tested call, and make sure the difference is correct. In other words, take into account the existing data, then see how the tested code changed things, without assuming anything about the existing data. It can be a bit of work to set up, but let's me test against a more "live" system.

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

In your case, are the specific IDs important? Could you generate the IDs on the fly, perhaps randomly, verify they're not already in use, then proceed?

在您的情况下,特定 ID 重要吗?您能否动态生成 ID,也许是随机生成的,验证它们尚未被使用,然后继续?

回答by Omar Estrella

When writing JUnit tests, you can override two specific methods: setUp() and tearDown(). In setUp(), you can set everything thats necessary in order to test your code so you dont have to set things up in each specific test case. tearDown() is called after all the test cases run.

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

If possible, you could set it up so you can open your database in the setUp() method and then have it clear everything from the tests and close it in the tearDown() method. This is how we have done all testing when we have a database.

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

Heres an example:

这是一个例子:

@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

回答by mwooten.dev

I agree with Brainimus if you're trying to test against data you have pulled from a database. If you're looking to test modifications made to the database, another solution would be to mock the database itself. There are multiple implementations of in-memory databases that you can use to create a temporary database (for instance during JUnit's setUp()) and then remove the entire database from memory (during tearDown()). As long as you're not using an vendor-specific SQL, then this is a good way to test modifying a database without touching your real production one.

如果您尝试针对从数据库中提取的数据进行测试,我同意 Brainimus 的观点。如果您要测试对数据库所做的修改,另一种解决方案是模拟数据库本身。内存数据库有多种实现,可用于创建临时数据库(例如在 JUnit 期间setUp()),然后从内存中删除整个数据库(在 期间tearDown())。只要您不使用特定于供应商的 SQL,那么这是一种在不接触实际生产数据库的情况下测试修改数据库的好方法。

Some good Java databases that offer in memory support are Apache Derby, Java DB(but it is really Oracle's flavor of Apache Derby again), HyperSQL(better known as HSQLDB) and H2 Database Engine. I have personally used HSQLDB to create in-memory mock databases for testing and it worked great, but I'm sure the others would offer similar results.

一些提供内存支持的优秀 Java 数据库是Apache DerbyJava DB(但它实际上又是 Oracle 的 Apache Derby 风格)、HyperSQL(更广为人知的是 HSQLDB)和H2 数据库引擎。我个人使用 HSQLDB 来创建用于测试的内存模拟数据库并且它工作得很好,但我相信其他人会提供类似的结果。

回答by ninesided

How about using something like DBUnit?

使用像DBUnit这样的东西怎么样?

回答by Hansgeorg Schwibbe

If you are using Spring, everything you need is the @DirtiesContext annotation on your test class.

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

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