【问题标题】:Refresh Repository Populator for each test为每个测试刷新存储库填充器
【发布时间】:2013-06-14 15:32:49
【问题描述】:

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

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

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

  1. 加载 Spring 上下文
  2. 初始化内存数据库并创建架构
  3. 测试前
    1. 使用存储库填充器加载测试数据
  4. 运行测试
  5. 测试后
    1. 从内存数据库中删除所有数据

我还没有找到一种简单的方法来做到这一点(特别是步骤 3.1)。我有一些想法,但我想看看其他人是否也尝试过。

【问题讨论】:

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


    【解决方案1】:

    一个有趣的挑战。听起来您想要的是 ResourceReaderRepositoryPopulator 的变体,它被 TestExecutionListener 以与 https://github.com/springtestdbunit/spring-test-dbunit/ 对 DbUnit 文件所做的相同方式挂钩。

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

    听起来你正在尝试做的是 spring-test-dbunit 的 Spring Data 版本,你应该能够基于该代码加上:ResourceReaderRepositoryPopulator 和 AbstractRepositoryPopulatorFactoryBean 来做。

    【讨论】:

    • 感谢您提供该链接。我会认真考虑使用 spring-test-dbunit。
    • 自然,存储库填充器比 DbUnit 具有显着优势,因为它允许您将 JSON 或 XML 用于 SQL 中将跨越许多表的聚合。例如,Spring Data 方法允许您轻松地从 JPA 切换到 NoSQL,或者在不同的 NoSQL 存储之间切换(在我的例子中,我在 MongoDB 和 fuzzydb 中使用了相同的数据
    【解决方案2】:

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

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public abstract class MockedApplicationContext {
    
      protected MockMvc mockMvc;
    
      @Autowired
      private WebApplicationContext webApplicationContext;
    
      @Autowired
      private ResourceReaderRepositoryPopulator populator;
    
      @Autowired
      private UserRepository userRepository;
    
      @Before
      public void setup() throws Exception {
        this.mockMvc = webAppContextSetup(webApplicationContext).build();
        userRepository.deleteAll();
        Repositories repositories = new Repositories(webApplicationContext);
        populator.populate(repositories);
      }
    }
    

    【讨论】:

      【解决方案3】:

      这是我能够开始工作的示例。

      我利用@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 中进行了一些小技巧。首先,我@Autowirepopulator 来获得对它的引用。我还需要对应用程序上下文的引用,所以我也需要@Autowire

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

      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);
          }
      
      }
      

      这确实感觉有点老套,但我认为它足够强大,直到我可以像@NealeU 建议的那样仔细查看 spring-test-dbunit。

      【讨论】:

        猜你喜欢
        • 2019-03-22
        • 2010-12-23
        • 2021-04-10
        • 2011-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多