刷新每个测试的存储库Populator

时间:2013-06-14 15:32:50

标签: java spring junit spring-data spring-data-jpa

我正在为Spring Batch作业编写集成测试。我希望在每次测试之前使用存储库填充程序将测试数据加载到内存数据库中。

我到目前为止发现的示例似乎表明存储库填充程序只会在初始化上下文时填充一次。这对我来说是个问题,因为每个测试方法都应该在存储库中加载它自己的测试数据。

基本上,这就是我要做的事情:

  1. 加载Spring上下文
  2. 初始化内存数据库并创建架构
  3. 测试前
    1. 使用repopsitory populator
    2. 加载测试数据
  4. 运行测试
  5. 测试后
    1. 删除内存数据库中的所有数据
  6. 我还没有找到一种简单的方法(特别是步骤3.1)。我有一些想法,但我想我是否会看到其他人也尝过这个。

3 个答案:

答案 0 :(得分:2)

一个有趣的挑战。听起来你想要的是ResourceReaderRepositoryPopulator的变种,它被TestExecutionListener挂钩,就像https://github.com/springtestdbunit/spring-test-dbunit/为DbUnit文件所做的那样。

删除操作只是在已插入的每个对象上调用repository.delete(object)。

听起来你正在尝试做的是spring-test-dbunit的Spring Data版本,你应该可以根据该代码加上:ResourceReaderRepositoryPopulator和AbstractRepositoryPopulatorFactoryBean。

答案 1 :(得分:0)

这是我能够开展工作的一个例子。

我利用@ContextConfiguration注释使用3个不同的文件启动上下文。 “MyJobConfiguration.xml”包含生产批处理作业定义,我不会在此处包含(与此问题无关)。 “BatchIntegrationTests.xml”是用于所有批量集成测试的常用上下文配置。它处理覆盖数据源以使用自动生成模式的内存数据库。再次,不包括在内。

“BatchJobIT.xml”是我设置我将使用的实际存储库填充程序的地方。不幸的是,repository:unmarshaller-populator标记需要指定locations属性;所以我需要提供一个'虚拟'xml文件(com / example / anyData.xml),当应用程序上下文启动时,存储库填充程序将加载该文件。我还指定使用XStream Unmarshaller并提供我在测试期间可能遇到的任何别名。

BatchJobIT.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--  
 *
 * Additional Configuration for BatchJobIT integration tests.
 * 
-->


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:repository="http://www.springframework.org/schema/data/repository"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <repository:unmarshaller-populator
        id="populator" locations="classpath:com/example/anyData.xml"
        unmarshaller-ref="xstreamMarshaller" />



    <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
        <property name="aliases">
            <props>
                <prop key="domainObjects">java.util.List</prop>
                <prop key="domainObject">com.example.domain.MyObject</prop>
            </props>
        </property>
    </bean>

</beans>

实际的测试类非常简单......在@Setup中有一点点hackiness。首先,我@Autowire populator来获取它的引用。我还需要对应用程序上下文的引用,所以我@Autowire也是如此。

对于此示例,我在类路径中搜索由测试类名称和测试方法名称限定的test-data.xml文件。然后,我将该测试数据文件设置为populator上的资源位置并触发ContextRefreshedEvent,这会导致populator重新填充它的存储库。

BatchJobIT.java

package com.example;

import static org.junit.Assert.assertNotNull;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.repository.init.UnmarshallerRepositoryPopulatorFactoryBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * This integration test will test a Spring Batch job.
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/modules/MyJobConfiguration.xml", "BatchIntegrationTests.xml", "BatchJobIT.xml" })
public class BatchJobIT {

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Autowired
    private UnmarshallerRepositoryPopulatorFactoryBean populator;

    //Keep track of the test name for use within the test methods
    @Rule
    public TestName name = new TestName();

    @Autowired
    private ApplicationContext applicationContext;

    @Before
    public void setUp() throws Exception {

        //Dynamically build the path to the test-data file. In this example, classpath will be searched for
        //'com/example/BatchJobIT.testBatchJob.test-data.xml'
        StringBuilder testDataFileName = new StringBuilder();
        testDataFileName.append(this.getClass().getCanonicalName());
        testDataFileName.replace(0, testDataFileName.length(), testDataFileName.toString().replaceAll("\\.", "/"));
        testDataFileName.append(".");
        testDataFileName.append(name.getMethodName());
        testDataFileName.append(".test-data.xml");

        //Set new resource location
        populator.getObject().setResourceLocation(testDataFileName.toString());
        //Send a context refreshed event to the populator which causes it to re-populate repositories
        populator.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
    }

    @After
    public void tearDown() throws Exception {

        //Delete test data from database
        EntityManager em = entityManagerFactory.createEntityManager();
        try {
            em.getTransaction().begin();
            em.createNativeQuery("TRUNCATE SCHEMA public AND COMMIT").executeUpdate();
            if (em.getTransaction().isActive()) {
                em.getTransaction().commit();
            }
        } catch (Exception e) {
            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }
            throw e;
        } finally {
            em.close();
        }
    }

    @Test
    public void testBatchJob() throws Exception {
        //Uncomment to launch hsqldb manager to inspect that data was loaded
        //org.hsqldb.util.DatabaseManagerSwing.main(new String[] { "--url", "jdbc:hsqldb:mem:testdb" });


        //Run test

        assertNotNull(populator);
    }

}

这确实感觉有点hacky,但我认为它足够强大,直到我能够像@NealeU建议的那样仔细观察spring-test-dbunit。

答案 2 :(得分:0)

在每次测试之前填充存储库的解决方案是一个基类,由感兴趣的测试类扩展。

:only