我必须使用Junit测试一些Thrift服务。当我将测试作为Thrift客户端运行时,服务会修改服务器数据库。我无法找到一个好的解决方案,可以在每次测试运行后清理数据库。 清理很重要,特别是因为ID必须是唯一的,目前从XML文件中读取。现在,我必须在运行测试后手动更改ID,以便下一组测试可以运行而不会在数据库中抛出主键冲突。如果我可以在每次测试运行后清理数据库,那么问题就完全解决了,否则我将不得不考虑其他解决方案,比如生成随机ID并在需要ID的地方使用它们。
编辑:我想强调一下,我正在测试一个写入数据库的服务,我没有直接访问数据库。但是,因为服务是我们的,我可以修改服务,以便在需要时提供任何清理方法。
答案 0 :(得分:15)
如果您使用的是Spring,那么您需要的只是测试类上的@DirtiesContext注释。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-context.xml")
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class MyServiceTest {
....
}
答案 1 :(得分:7)
除非您测试特定的数据库操作(例如,验证您可以查询或更新数据库),否则您的JUnits不应该写入真实数据库。相反,你应该模拟数据库类。这样,您实际上不必连接和修改数据库,因此不需要进行清理。
您可以通过几种不同的方式模拟您的课程。您可以使用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);
}
答案 2 :(得分:5)
如何使用DBUnit之类的内容?
答案 3 :(得分:4)
Spring的单元测试框架具有处理JDBC的广泛功能。一般方法是单元测试在事务中运行,并且(在测试之外)一旦测试完成,事务就会回滚。
这样做的好处是能够使用您的数据库及其架构,但不会对数据进行任何直接更改。当然,如果你在测试中实际执行了提交,那么所有的赌注都会被取消!
有关更多阅读,请查看有关使用JDBC进行集成测试的Spring's documentation。
答案 4 :(得分:4)
编写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
答案 5 :(得分:2)
假设您有权访问数据库:另一个选择是在测试之前创建数据库备份,并在测试之后从该备份恢复。这可以自动化。
答案 6 :(得分:1)
如果您使用的是Spring + Junit 4.x,则无需在DB中插入任何内容。 看着 AbstractTransactionalJUnit4SpringContextTests上课。
另请查看Spring文档以获取JUnit支持。
答案 7 :(得分:1)
这有点严苛,但我通常的目标是在每次测试方法执行之前消灭数据库(或者只是我感兴趣的表)。当然,当我进入更多集成类型的测试时,这不会起作用。
如果我无法控制数据库,说我想验证在给定调用之后创建的行数是否正确,那么测试将计算测试调用之前和之后的行数,并确保差异是正确的。换句话说,考虑现有数据,然后看看测试代码如何改变事物,而不假设现有数据。设置可能需要一些工作,但让我测试一个更“实时”的系统。
在您的情况下,具体ID是否重要?你可以动态生成ID,可能是随机的,验证它们还没有被使用,然后继续?
答案 8 :(得分:1)
如果您尝试对数据库中提取的数据进行测试,我同意Brainimus的观点。如果您正在测试对数据库进行的修改,另一种解决方案是模拟数据库本身。您可以使用多种内存数据库实现来创建临时数据库(例如在JUnit的setUp()
期间),然后从内存中删除整个数据库(在tearDown()
期间)。只要您没有使用特定于供应商的SQL,那么这是一种测试修改数据库而不需要触及真实生产数据库的好方法。
在内存支持中提供的一些优秀Java数据库是Apache Derby,Java DB(但它确实是Oracle的Apache Derby风格),HyperSQL(更好地称为HSQLDB)和{ {3}}。我个人使用HSQLDB来创建用于测试的内存模拟数据库,它运行良好,但我确信其他人会提供类似的结果。