【问题标题】:Using @PostConstruct in a test class causes it to be called more than once在测试类中使用 @PostConstruct 会导致它被多次调用
【发布时间】:2018-08-27 10:34:24
【问题描述】:

我正在编写集成测试来测试我的端点,并且需要在构建后立即在数据库中设置一个用户,以便 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
    }

}

然而,@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;
    }
}

【问题讨论】:

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


    【解决方案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 {
     ...
    }
    

    请注意,如果要将来自 Spring Boot 测试类的 TestExecutionListeners 与当前类的 @TestExecutionListeners 中定义的 TestExecutionListeners 合并,MergeMode.MERGE_WITH_DEFAULTS 很重要。
    默认值为MergeMode.REPLACE_DEFAULTS

    【讨论】:

      猜你喜欢
      • 2014-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-10
      • 2019-01-04
      • 1970-01-01
      • 1970-01-01
      • 2021-06-23
      相关资源
      最近更新 更多