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