Использование @PostConstruct в тестовом классе вызывает его несколько раз
Я пишу интеграционные тесты для проверки моих конечных точек и мне нужно настроить пользователя в базе данных сразу после создания, чтобы аннотация 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;
}
}
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
имеет значение, если вы хотите объединить TestExecutionListener
из тестового класса Spring Boot с TestExecutionListener
определены в @TestExecutionListeners
текущего класса.
Значением по умолчанию является MergeMode.REPLACE_DEFAULTS
,