问题描述
我正在编写集成测试以测试我的端点,并且需要在构造后立即在数据库中设置用户,以便Spring Security Test批注@WithUserDetails
可以从数据库中收集用户.
I am writing integration tests to test my endpoints and need to setup a User in the database right after construct so the Spring Security Test annotation @WithUserDetails
has a user to collect from the database.
我的班级设置是这样的:
My class setup is like this:
@RunWith(value = SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@WithUserDetails(value = "[email protected]")
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
调用.
However the @PostConstruct
method is called for every annotated @Test
, even though we are not instantiating the main class again.
因为我们使用Spring Security Test(@WithUserDetails
),所以我们需要用户持久化到数据库,然后才能使用JUnit批注@Before
.我们也不能使用@BeforeClass
,因为我们依赖@Autowired
服务:aService
.
Because we use Spring Security Test (@WithUserDetails
) we need the user persisted to the database BEFORE we can use the JUnit annotation @Before
. We cannot use @BeforeClass
either because we rely on the @Autowired
service: aService
.
我发现的一个解决方案是使用变量来确定我们是否已经设置了数据(请参见下文),但这听起来很脏,并且还有更好的方法.
A solution I found would be to use a variable to determine if we have already setup the data (see below) but this feels dirty and that there would be a better way.
@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
.
TLDR : Keep your way for the moment. If later the boolean flag is repeated in multiple test classes create your own TestExecutionListener
.
在JUnit中,每个执行的测试方法都会调用测试类构造函数.
因此,为每个测试方法调用@PostConstruct
是很有意义的.
根据JUnit和Spring的功能,您的解决方法还不错.特别是因为您是在基本测试类中进行的.
In JUnit, the test class constructor is invoked at each test method executed.
So it makes sense that @PostConstruct
be invoked for each test method.
According to JUnit and Spring functioning, your workaround is not bad. Specifically because you do it in the base test class.
以一种不太肮脏的方式,您可以用@TestExecutionListeners
注释测试类并提供自定义的TestExecutionListener
,但是在您一次使用它的时候,这似乎有点过头了.
在您没有/想要基类并且想要在多个类中添加布尔标志的情况下,使用自定义TestExecutionListener
可能很有意义.这是一个例子.
As less dirty way, you could annotate your test class with @TestExecutionListeners
and provide a custom TestExecutionListener
but it seem overkill here as you use it once.
In a context where you don't have/want the base class and you want to add your boolean flag in multiple classes, using a custom TestExecutionListener
can make sense.Here is an example.
自定义TestExecutionListener:
Custom 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 protected]")
@TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS,
value=MyMockUserTestExecutionListener.class)
public abstract class IntegrationTests {
...
}
请注意,如果要将来自Spring Boot测试类的TestExecutionListener
与当前类的@TestExecutionListeners
中定义的TestExecutionListener
合并,则MergeMode.MERGE_WITH_DEFAULTS
很重要.
默认值为MergeMode.REPLACE_DEFAULTS
.
Note that MergeMode.MERGE_WITH_DEFAULTS
matters if you want to merge TestExecutionListener
s coming from the Spring Boot test class with TestExecutionListener
s defined in the @TestExecutionListeners
of the current class.
The default value is MergeMode.REPLACE_DEFAULTS
.
这篇关于在测试类中使用@PostConstruct导致多次调用它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!