我意识到最佳实践可能建议在每个@Test
方法上加载测试数据,但是对于DBUnit来说这可能会非常慢,所以我想出了以下解决方案,每个类只加载一次:
虽然下面的代码有效,但让我烦恼的是我的Test类有静态方法beforeClassWithApplicationContext()
,但它不能属于一个接口,因为它是静态的。因此,我使用Reflection以非类型安全的方式使用。有更优雅的解决方案吗?
/**
* My Test class
*/
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, DbunitLoadOnceTestExecutionListener.class})
@ContextConfiguration(locations={"classpath:resources/spring/applicationContext.xml"})
public class TestClass {
public static final String TEST_DATA_FILENAME = "Scenario-1.xml";
public static void beforeClassWithApplicationContext(ApplicationContext ctx) throws Exception {
DataSource ds = (DataSource)ctx.getBean("dataSourceXyz");
IDatabaseConnection conn = new DatabaseConnection(ds.getConnection());
IDataSet dataSet = DbUnitHelper.getDataSetFromFile(conn, TEST_DATA_FILENAME);
InsertIdentityOperation.CLEAN_INSERT.execute(conn, dataSet);
}
@Test
public void somethingToTest() {
// do stuff...
}
}
/**
* My new custom TestExecutioner
*/
public class DbunitLoadOnceTestExecutionListener extends AbstractTestExecutionListener {
final String methodName = "beforeClassWithApplicationContext";
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
super.beforeTestClass(testContext);
Class<?> clazz = testContext.getTestClass();
Method m = null;
try {
m = clazz.getDeclaredMethod(methodName, ApplicationContext.class);
}
catch(Exception e) {
throw new Exception("Test class must implement " + methodName + "()", e);
}
m.invoke(null, testContext.getApplicationContext());
}
}
我有另外一个想法可能是创建一个静态单例类来保存对ApplicationContext的引用并从DbunitLoadOnceTestExecutionListener.beforeTestClass()
填充它。然后,我可以从TestClass上定义的标准@BeforeClass
方法中检索该单例引用。上面回调每个TestClass的代码看起来有点混乱。
答案 0 :(得分:2)
在Matt和JB的有用反馈之后,这是一个更简单的解决方案,可以达到预期的效果
/**
* My Test class
*/
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, DbunitLoadOnceTestExecutionListener.class})
@ContextConfiguration(locations={"classpath:resources/spring/applicationContext.xml"})
public class TestClass {
private static final String TEST_DATA_FILENAME = "Scenario-1.xml";
// must be static
private static volatile boolean isDataSetLoaded = false;
// use the Qualifier to select a specific dataSource
@Autowired
@Qualifier("dataSourceXyz")
private DataSource dataSource;
/**
* For performance reasons, we only want to load the DBUnit data set once per test class
* rather than before every test method.
*
* @throws Exception
*/
@Before
public void before() throws Exception {
if(!isDataSetLoaded) {
isDataSetLoaded = true;
IDatabaseConnection conn = new DatabaseConnection(dataSource.getConnection());
IDataSet dataSet = DbUnitHelper.getDataSetFromFile(conn, TEST_DATA_FILENAME);
InsertIdentityOperation.CLEAN_INSERT.execute(conn, dataSet);
}
}
@Test
public void somethingToTest() {
// do stuff...
}
}
不再需要课程DbunitLoadOnceTestExecutionListener
并已将其删除。它只是表明阅读所有花哨的技术有时可能会影响你自己的判断:o)
答案 1 :(得分:1)
不是专家,但是在验证它实现了适当的接口之后,你无法在prepareTestInstance()
中调用测试对象的实例方法,并且只有在第一次调用prepareTestInstance时才调用此方法测试这个类的实例。你只需要保留一组已经看过的类:
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
MyDbUnitTest instance = (MyDbUnitTest) getTestInstance();
if (!this.alreadySeenClasses.contains(instance.getClass()) {
instance.beforeClassWithApplicationContext(testContext.getApplicationContext());
this.alreadySeenClasses.add(instance.getClass());
}
}