Junit Spring避免加载两次应用程序上下文数据源

时间:2019-02-07 08:30:02

标签: java spring applicationcontext

我有以下配置类:

@ComponentScan(
        basePackages = { 
                "mypackage.controller",
                "mypackage.service",
                "mypackage.repository" 
        }
)
@TestPropertySource(locations="classpath:configuration.properties")
@Import({
    H2Configuration.class
})
public class TestConfiguration {
}

@Configuration
public class H2Configuration {

    @Bean
    public DataSource dataSource() throws SQLException {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        EmbeddedDatabase db = builder
                .setType(EmbeddedDatabaseType.H2)
                .addScript("h2/create.sql")
                .addScript("h2/insert.sql")
                .build();
        db.getConnection().setAutoCommit(false);
        return db;
    }

}

我有两个类测试:

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

    @Autowired
    MyFirstRepositoryImpl repository;

    @Before
    public void initTest() {
    }

    @Test(expected = NullPointerException.class)
    public void testNullRecords() {
        repository.foo(null, null);
    }
}


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

    @Autowired
    MySecondRepositoryImpl repository;

    @Before
    public void initTest() {
    }

    @Test(expected = NullPointerException.class)
    public void testSomethingNullRecords() {
        repository.something(null, null);
    }
}

如果我对每个课程都运行一次junit测试,一切都会顺利进行。

在全新安装阶段,测试失败,因为应用程序上下文已初始化两次。

例如,它尝试两次创建h2表,并两次执行insert.sql脚本。

我需要做什么来初始化h2数据库,因此仅一次应用程序上下文?

谢谢

4 个答案:

答案 0 :(得分:3)

我认为您可以开始查看有关Integration Testing的Spring文档。

将事务性测试用于集成测试(@Transactional)也是一种好习惯,该测试在每个测试结束时都会回滚:请参见Transaction Management

为避免为每个测试类重新创建ApplicationContext的开销,可以按照以下说明使用缓存:Context Caching

对于与嵌入式数据库的集成测试,您还可以找到文档:Testing Data Access Logic with an Embedded Database。 上一个链接中的注释,与您的用例匹配:

  

但是,如果您希望创建一个共享的嵌入式数据库   在测试套件中,请考虑使用Spring TestContext Framework   在Spring中将嵌入式数据库配置为bean   如创建嵌入式数据库中所述的ApplicationContext   使用Spring XML并以编程方式创建嵌入式数据库。

我希望您会找到一些有用的参考。

答案 1 :(得分:2)

在单元测试中,您必须保证每个测试都独立于可重复的hance上下文。因此,只加载一次上下文不是一个好主意。最好在执行后重置。为此,您可以在测试类中使用 @DirtiesContext(classMode = ClassMode.AFTER_CLASS)

因此,当下一个junit类启动时,您将强制上下文重新启动

答案 2 :(得分:2)

我从Embedded Database Support Spring Boot 文档中找到了另一个好提示:

他们说:

  

如果您在测试中使用此功能,则可能会注意到   整个测试套件都会重复使用同一数据库,无论   您使用的应用程序上下文数。如果你想确定   每个上下文都有一个单独的嵌入式数据库,则应设置   将spring.datasource.generate-unique-name设置为true。

因此,要使每个EmbeddedDatabase都是唯一的,您可以尝试使用:

EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
                      .generateUniqueName(true)
                      ...
                      .build();

答案 3 :(得分:1)

因此失败的原因是,在作为清洁/安装的一部分运行测试时,数据库(H2)位于内存中。运行第一个测试后,创建/插入脚本已经执行。在此之后执行任何后续测试,将导致重新执行相同的脚本,并且会发生错误。

使用DROP TABLE IF EXISTS <table name>;更新您的创建脚本。这样可以确保删除该表然后重新创建。

注意:我不确定为什么您明确指定了AnnotationConfigContextLoader。我认为,如果没有这些内容,运行程序SpringJUnit4ClassRunner将缓存未更改的上下文。我不知道是不是这种情况。