你能想到一个更好的方法来只使用Spring为每个测试类加载一次DBbUnit吗?

时间:2011-11-29 15:19:03

标签: spring junit dbunit

我意识到最佳实践可能建议在每个@Test方法上加载测试数据,但是对于DBUnit来说这可能会非常慢,所以我想出了以下解决方案,每个类只加载一次:

  1. 每个测试类仅加载一次数据集
  2. 从ApplicationContext
  3. 支持多个数据源和那些未命名为“dataSource”的数据源
  4. 回滚未严格要求的插入的DBUnit数据集
  5. 虽然下面的代码有效,但让我烦恼的是我的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的代码看起来有点混乱。

2 个答案:

答案 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());
    }
}