Использование @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,

Другие вопросы по тегам