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

标签 java spring spring-boot junit4 spring-test

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

我的类(class)设置是这样的:

@RunWith(value = SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@WithUserDetails(value = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2d48404c44416d4c49495f485e5e034e4240" rel="noreferrer noopener nofollow">[email protected]</a>")
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
    }

}

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

因为我们使用 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;
    }
}

最佳答案

TLDR:暂时保持你的方式。如果稍后在多个测试类中重复 boolean 标志,请创建您自己的 TestExecutionListener

在 JUnit 中,在执行每个测试方法时都会调用测试类构造函数。
因此,为每个测试方法调用 @PostConstruct 是有意义的。
根据 JUnit 和 Spring 的功能,您的解决方法还不错。具体来说是因为您在基础测试类中执行此操作。

作为一种不太肮脏的方式,您可以使用 @TestExecutionListeners 注释您的测试类,并提供自定义 TestExecutionListener 但当您使用它一次时,它在这里似乎有点过分了。
在您没有/不需要基类并且想要在多个类中添加 boolean 标志的情况下,使用自定义 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 = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="17727a767e7b57767373657264643974787a" rel="noreferrer noopener nofollow">[email protected]</a>")
@TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS, 
                        value=MyMockUserTestExecutionListener.class) 
public abstract class IntegrationTests {
 ...
}

请注意,如果您想要将来自 Spring Boot 测试类的 TestExecutionListener 与定义在当前类的@TestExecutionListeners
默认值为MergeMode.REPLACE_DEFAULTS

关于java - 在测试类中使用@PostConstruct会导致它被多次调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49353266/

相关文章:

javascript - Azure 单点登录发布请求时出现 403 禁止错误

java - 如何在java spring boot中使用多个join sql查询

java - 用java读取hex文件并将其转换为ascii

Java 卡布局,KeyListener 停止工作

java - 将 C 或 C++ 常量转换为 Java

java - 使用 Spring 进行 MongoDB 复制

java - 如何在 Spring Data rest 中返回 List<String> 或 String

java - 用单反斜杠替换双反斜杠

java - Spring JPA deleteInBatch 导致 StackOverflow

java - RestTemplate postForEntity 遇到 java.io.IOException : Premature EOF error