【问题标题】:How to efficiently create a base test class for spring mvc integration tests如何有效地为 spring mvc 集成测试创建基础测试类
【发布时间】:2013-07-11 23:40:22
【问题描述】:

我有一系列集成测试,我想用它们来测试我的 spring-mvc/spring-data-jpa 堆栈。不幸的是,构建时间是荒谬的,而且每次新的集成测试只会变得更糟。看起来好像每个单独的测试都在通过创建嵌入式数据库、创建 bean 等的开销。

我有一个基础测试类:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( loader = AnnotationConfigContextLoader.class, classes = { JpaConfig.class } )
public class BaseItegration {

private static EmbeddedDatabase database;

@BeforeClass
public static void setUp() throws Exception {
    database = new EmbeddedDatabaseBuilder().setType( EmbeddedDatabaseType.H2 ).setName( "mydb" )
        .addScript( "classpath:embeddedDatabase.sql" ).build();
}


@Test
    public void testInit() {
        Assert.assertNotNull( database );
    }

我的 JpaConfig.java 在哪里:

@Configuration
@EnableTransactionManagement
@ComponentScan( basePackages = { "org.myproject.service", "org.myproject.utility",
      "org.myproject.controller", "org.myproject.utility.startup",
      "org.myproject.security" } )
@ImportResource( { "classpath:applicationContext.xml", "classpath:myproject-spring-security.xml" } )
public class JpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {...}
    @Bean
    public DataSource dataSource() {...}

    <etc>
}

最后我尝试使用它,例如:

@TransactionConfiguration( defaultRollback = true )
@Transactional( propagation = Propagation.NESTED )
public class TestContactTypesIT extends BaseItegration {

    @Autowired
    private ContactTypeRefRepository contactTypeRepository;
    @Test
    public void testRepositoryNotNull() {
        Assert.assertNotNull( contactTypeRepository );
    }

...}

查看构建日志时,我可以看到每个测试的应用程序正在初始化。有没有办法让 BaseIntegrationTest 只启动一个并且每个测试都使用该应用程序上下文和嵌入式数据库?

========

更新

我将我的 JpaConfig 更改为:

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType( EmbeddedDatabaseType.H2 ).setName( "mydb" )
                .addScript( "classpath:embeddedDatabase.sql" ).build();
}

我的 BaseIntegration 现在是空的:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( loader = AnnotationConfigContextLoader.class, classes = { JpaConfig.class } )
public abstract class BaseItegration {

}

这是因错误而失败的测试之一:

InvalidDataAccessResourceUsageException(表“ADDRESSTYPEREF”不是 成立; SQL语句:

@TransactionConfiguration( defaultRollback = true )
@Transactional( propagation = Propagation.NESTED )
public class TestSeedAddressTypesIT extends BaseItegration {

    @Autowired
    private AddressTypeRefRepository addressTypeRepository;

    @Autowired
    private SeedAddressTypes seedAddressTypes;

    // hack because we can't do a BeforeClass with Autowired
    private boolean seeded = false;

    @Test
    public void testRepositoryNotNull() {
        Assert.assertNotNull( addressTypeRepository );
    }

    @Test
    public void testPopulatedDB() {
        if (!seeded) {
            seedAddressTypes.seed();
            seeded = true;
        }
        List<AddressTypeRef> addressTypes = addressTypeRepository.findAll();
        Assert.assertEquals( 5, addressTypes.size() );

    }
}

但是,所有集成测试似乎都重新创建了 applicationContext 和嵌入式数据库。虽然构建日志没有显示正在创建的数据源,但我确实看到每个集成测试都会创建一个新的 log4j 文件,并且构建 45 个测试需要 15 分钟。

【问题讨论】:

  • AFAIK,不应为每个测试重新创建弹簧上下文。你如何运行你的测试?您确定没有将测试运行器配置为为每个测试生成一个新的 JVM?
  • 我正在使用“mvn clean verify”来构建 WAR 文件。

标签: spring-mvc integration-testing spring-mvc-test


【解决方案1】:

似乎@BeforeClass 意味着使用@BeforeClass 注释的任何静态方法在类中的任何测试方法之前运行。因此,如果您有一个包含多个类的测试套件,它运行的次数与您的测试类的数量一样多。

为什么不在单独的 applicationContexts 中初始化您的嵌入式数据库和真实数据库,并在您的测试中只添加嵌入式数据库 applicationContext?

例如(对不起,我对java config风格不熟悉):

Listing-1:embedded-database.xml

<jdbc:embedded-database id="dataSource">
    <jdbc:script location="classpath:schema.sql"/>
    <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

清单 2:BaseItegration.java

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( locations = { "classpath:your-application-without-a-real-database.xml", "classpath:"embedded-database.xml" } )
public class BaseItegration {


}

然后使用 applicationContext 启动嵌入式数据库(因此只有一次)。

另一个解决方案是使用 Spring Profile,您不必在单独的 xmls 中分离数据库。

Listing-1:your-application-context.xml

<bean profile="integrationTest">
    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:schema.sql"/>
        <jdbc:script location="classpath:test-data.sql"/>
    </jdbc:embedded-database>
<bean>

<bean profile="production">
    //omitted 
</bean>

清单 2:BaseItegration.java

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( locations = { "classpath:your-application-context.xml" } )
@ActiveProfiles("integrationTest")
public class BaseItegration {


}

记得在你的 web.xml 中激活生产。

更新:

将您的 BaseIntegration 修改为抽象类以解决无 @Test 方法问题。数据源是在您的日志中创建的吗?

【讨论】:

  • 我修改了 BaseItegration 使其不再创建嵌入式数据库,还更改了 JpaConfig 以创建嵌入式数据库作为数据源。但是现在我所有的集成测试都失败了,因为他们找不到数据库。我改变了我的问题以反映这些变化
  • @sonoerin 已更新,您可以发布您的 BaseIntegration 子类之一吗?
  • 我用抽象基类和示例测试更新了我的帖子。我没有看到正在创建数据源,但在每次测试时都会创建日志文件,并且 85 次测试的总构建时间为 15 分钟。
猜你喜欢
  • 1970-01-01
  • 2016-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-19
  • 2013-01-11
相关资源
最近更新 更多