在测试类中使用@PostConstruct会导致它被多次调用

时间:2018-03-18 22:01:26

标签: java spring spring-boot junit4 spring-test

我正在编写集成测试来测试我的端点,并且需要在构造之后立即在数据库中设置User,因此Spring Security Test注释@WithUserDetails有一个用户从数据库中收集。

我的课程设置如下:

@RunWith(value = SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@WithUserDetails(value = "email@address.com")
public abstract class IntegrationTests {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private Service aService;

    @PostConstruct
    private void postConstruct() throws UserCreationException {
        // Setup and save user data to the db using autowired service "aService"

        RestAssuredMockMvc.mockMvc(mockMvc);
    }

    @Test
    public void testA() {
        // Some test
    }

    @Test
    public void testB() {
        // Some test
    }

    @Test
    public void testC() {
        // Some test
    }

}

然而,{strong>每个带注释的@PostConstruct都会调用@Test方法,即使我们没有再次实例化主类。

因为我们使用Spring Security Test(@WithUserDetails),所以在我们可以使用JUnit注释@Before之前,我们需要用户持久化到数据库。我们无法使用@BeforeClass因为我们依赖@Autowired服务:aService

我找到的解决方案是使用变量来确定我们是否已经设置了数据(见下文),但这感觉很脏并且会有更好的方法。

@PostConstruct
private void postConstruct() throws UserCreationException {
    if (!setupData) {
        // Setup and save user data to the db using autowired service "aService"

        RestAssuredMockMvc.mockMvc(mockMvc);
        setupData = true;
    }
}

1 个答案:

答案 0 :(得分:1)

TLDR:暂时保持原状。如果稍后在多个测试类中重复布尔标志,则创建自己的TestExecutionListener

在JUnit中,在执行的每个测试方法中调用测试类构造函数 因此,为每种测试方法调用@PostConstruct是有道理的 根据JUnit和Spring的功能,你的解决方法也不错。特别是因为你在基础测试类中这样做了。

尽管不那么脏,但您可以使用@TestExecutionListeners为您的测试类添加注释并提供自定义TestExecutionListener,但在此处使用它时,它似乎有点过分。
在您没有/想要基类并且想要在多个类中添加布尔标志的上下文中,使用自定义TestExecutionListener可能是有意义的。 这是一个例子。

自定义TestExecutionListener:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

public  class MyMockUserTestExecutionListener extends AbstractTestExecutionListener{

    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        MyService myService = testContext.getApplicationContext().getBean(MyService.class);
        // ... do my init
    }
}

测试课程已更新:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@WithUserDetails(value = "email@address.com")
@TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS, 
                        value=MyMockUserTestExecutionListener.class) 
public abstract class IntegrationTests {
 ...
}

请注意,MergeMode.MERGE_WITH_DEFAULTS对于将来自Spring Boot测试类的TestExecutionListener与当前类的TestExecutionListener中定义的@TestExecutionListeners进行合并很重要。<登记/> 默认值为MergeMode.REPLACE_DEFAULTS