Интеграционное тестирование в Spring: TestContext Framework, внедрение зависимостей с помощью SpringExtension

SpringExtension реализует API расширения ParameterResolver из JUnit Jupiter, что позволяет Spring обеспечивать внедрение зависимостей для конструкторов тестов, методов тестирования и методов обратного вызова жизненного цикла тестирования.

В частности, SpringExtension может внедрять зависимости из ApplicationContext теста в конструкторы и методы тестов, аннотированные с помощью @BeforeAll, @AfterAll, @BeforeEach, @AfterEach, @Test, @RepeatedTest, @ParameterizedTest и других.

Внедрение конструктора

Если конкретный параметр в конструкторе для тестового класса JUnit Jupiter имеет тип ApplicationContext (или его подтип) или аннотируется или метааннотируется с помощью @Autowired, @Qualifier или @Value, Spring вводит значение для этого конкретного с соответствующим bean-компонентом или значением из ApplicationContext теста.

Spring также может быть настроен на автоматическое подключение всех аргументов для конструктора тестового класса, если конструктор считается автоматическим. Конструктор считается автоматическим, если выполняется одно из следующих условий (в порядке приоритета).

  • Конструктор помечен @Autowired.
  • @TestConstructor присутствует или мета-присутствует в тестовом классе с атрибутом autowireMode, установленным на ALL.
  • Режим autowire конструктора тестов по умолчанию изменен на ALL.

Если конструктор для тестового класса считается автоматическим (autowirable, автопривязываемым), Spring берет на себя ответственность за разрешение аргументов для всех параметров в конструкторе. Следовательно, никакой другой ParameterResolver, зарегистрированный в JUnit Jupiter, не может разрешить параметры для такого конструктора.

Внедрение конструктора для тестовых классов не должно использоваться вместе с поддержкой JUnit Jupiter @TestInstance(PER_CLASS), если @DirtiesContext используется для закрытия ApplicationContext теста до или после тестовых методов.

Причина в том, что @TestInstance(PER_CLASS) инструктирует JUnit Jupiter кэшировать тестовый экземпляр между вызовами тестовых методов. Следовательно, тестовый экземпляр сохранит ссылки на bean-компоненты, которые были изначально введены из ApplicationContext, который впоследствии был закрыт. Поскольку конструктор для тестового класса в таких сценариях будет вызываться только один раз, внедрение зависимостей больше не произойдет, а последующие тесты будут взаимодействовать с bean-компонентами из закрытого ApplicationContext, что может привести к ошибкам.

Чтобы использовать @DirtiesContext с режимами "до метода тестирования" ("before test method") или "после метода тестирования" ("after test method") в сочетании с @TestInstance(PER_CLASS), необходимо настроить зависимости из Spring, которые будут передаваться через внедрение поля или установщика, чтобы их можно было повторно вводить между вызовами методов теста.

В следующем примере Spring внедряет bean-компонент OrderService из ApplicationContext, загруженного из TestConfig.class, в конструктор OrderServiceIntegrationTests.

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

    private final OrderService orderService;

    @Autowired
    OrderServiceIntegrationTests(OrderService orderService) {
        this.orderService = orderService;
    }

    // тесты, использующие внедренный OrderService
}

Обратите внимание, что эта функция позволяет тестовым зависимостям быть окончательными и, следовательно, неизменяемыми.

Если свойство spring.test.constructor.autowire.mode выставлено all, мы можем опустить объявление @Autowired в конструкторе в предыдущем примере, что приведет к следующему.

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

    private final OrderService orderService;

    OrderServiceIntegrationTests(OrderService orderService) {
        this.orderService = orderService;
    }

    // тесты, использующие внедренный OrderService
}

Внедрение метода

Если параметр в методе тестирования JUnit Jupiter или методе обратного вызова жизненного цикла теста имеет тип ApplicationContext (или его подтип) или аннотируется или метааннотируется с помощью @Autowired, @Qualifier или @Value, Spring внедряет значение для этого конкретного параметра с соответствующим bean-компонентом из ApplicationContext теста.

В следующем примере Spring вставляет OrderService из ApplicationContext, загруженного из TestConfig.class, в тестовый метод deleteOrder():

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

    @Test
    void deleteOrder(@Autowired OrderService orderService) {
        // используем orderService 
        // из ApplicationContext теста
    }
}

Благодаря надежности поддержки ParameterResolver в JUnit Jupiter, вы также можете внедрить несколько зависимостей в один метод не только из Spring, но и из самого JUnit Jupiter или других сторонних расширений.

В следующем примере показано, как Spring и JUnit Jupiter одновременно внедряют зависимости в метод тестирования placeOrderRepeatedly().

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

    @RepeatedTest(10)
    void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
            @Autowired OrderService orderService) {

        // используем orderService 
        // из ApplicationContext теста
        // и repetitionInfo из JUnit Jupiter
    }
}

Обратите внимание, что использование @RepeatedTest из JUnit Jupiter позволяет методу тестирования получить доступ к RepetitionInfo.


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки