Как эффективно использовать вложенные классы конфигурации для внедрения зависимостей, когда у меня много классов SpringBootTest - PullRequest
0 голосов
/ 28 октября 2019

Этот вопрос является продолжением Можно ли использовать код для управления решениями по разрешению зависимостей, принимаемыми ApplicationContext в Spring Boot?

Принятый ответ - определить вложенный класс в каждом @SpringBootTest проверить класс прибора, добавить к нему @TestConfiguration и определить в нем фабричный метод для каждого компонента, который необходимо разрешить. Влияние вложенных классов распространяется на тестовое устройство, влияющее на все тесты в устройстве, но не влияющее на тесты, определенные в других приборах.

Это обеспечивает детальный контроль над зависимостями, вводимыми в компоненты при запускетесты в каждом тестовом приборе.

Проблема этого подхода заключается в том, что он требует добавления вложенного класса резолвера в каждый класс тестового прибора. Это не масштабируется. Рассмотрим проект с 10 тестовыми приборами. 9 из них используют одни и те же внедренные зависимости, и только 10-е требует другой реализации только для одного конкретного интерфейса.

В этом случае мне нужно будет скопировать класс конфигурации теста в 9 классов тестовых приборов и использовать вторую конфигурациюкласс только для 10-го теста.

Мне нужен более масштабируемый способ сделать это. Например, в приведенном выше случае я хотел бы иметь возможность определить два класса конфигурации, по одному для каждой из двух конфигураций, используемых тестовыми приборами. Тогда я хотел бы иметь возможность указать для каждого тестового прибора, какой из двух классов конфигурации следует использовать. Я попытался:

  1. Я попытался импортировать вложенный класс конфигурации одного текстового прибора в другой тестовый прибор, используя аннотацию @Import в последнем, но при этом класс конфигурации игнорируется впоследний.
  2. Я также попытался переместить вложенный класс конфигурации на верхний уровень, чтобы его можно было использовать для всех тестовых приборов, которые явно не определяют другой класс как вложенный класс, но в этом случае класс конфигурации игнорируется. всеми тестовыми приборами.

Итак, в общем, я ищу эффективный способ, который позволил бы мне написать каждый класс конфигурации только один раз, а затем выборочно применить один к каждому классу SpringBootTest без необходимости копировать его.

1 Ответ

0 голосов
/ 28 октября 2019

После некоторых экспериментов я достиг следующего решения. Я добавлю все детали, суммирующие то, что я узнал в предыдущем вопросе тоже.

Фон

  1. У нас есть два интерфейса: IClient и IServer
  2. Существует две реализации IClient: RealClient и MockClient.
  3. Существует две реализации IServer: RealServer и MockServer.

Требования

  1. Производствокод (в main / java) должен использовать реальные реализации обоих.
  2. Test Fixtures (аннотирован @SpringBootTest в test / java)

    • InterfaceTests определяет тесты, которые должныиспользуйте MockServer и MockClient
    • ClientTests определяет тесты, которые должны использовать MockServer и RealClient для тестирования RealClient.
    • ServerTests определяет тесты, которые должны использовать MockClient и RealServer для тестирования RealServer.
    • IntegrationTests определяет тесты, которые должны использовать RealServer и RealClient

Из приведенного вышеПонятно, что существует четыре комбинации макет / реальный клиент / сервер, и каждая комбинация необходима в некоторой области кода.

Решение

В этом решении используются аннотации @Configuration и @TestConfiguration. чтобы реализовать эти требования без дублирования кода.

  1. НЕ комментируйте интерфейсы и их реализации @ Component
  2. В main / java реализуйте класс конфигурации следующим образом:

@Configuration
public class RealInjector {
    @Bean
    public IServer createServer(){
        return new RealServer();
    }

    @Bean
    public IClient createClient(){
        return new RealClient();
    }
}

В рамках теста / java реализуются эти три класса конфигурации теста
@TestConfiguration
public class AllMockInjector {
    @Bean
    public IServer createServer(){
        return new MockServer();
    }

    @Bean
    public IClient createClient(){
        return new MockClient();
    }
}

@TestConfiguration
public class MockServerInjector{
    @Bean
    public IServer createServer(){
        return new MockServer();
    }

    @Bean
    public IClient createClient(){
        return new RealClient();
    }
}

@TestConfiguration
public class MockClientInjector{
    @Bean
    public IServer createServer(){
        return new RealServer();
    }

    @Bean
    public IClient createClient(){
        return new MockClient();
    }
}

Аннотируйте тестовое устройство InterfaceTests следующим образом:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AllMockInjector.class})
public class InterfaceTests { ... }
Аннотируйте тестовое устройство ClientTests следующим образом:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockServerInjector.class})
public class ClientTests { ... }
Аннотируйте тестовое устройство ServerTests следующим образом:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockClientInjector.class})
public class ServerTests { ... }
Аннотируйте тестовое устройство IntegrationTests следующим образом:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {RealInjector.class})
public class IntegrationTests { ... }

Наконец

Чтобы классы конфигурации теста переопределили класс конфигурации RealInjector из main / java, нам нужноустановите свойство:

spring.main.allow-bean-definition-overriding=true 

Один из способов сделать это - аннотировать каждое из приведенных выше тестовых приспособлений следующим образом:

@SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
class TestFixture { ... }

, но это довольно многословно, особенно если у вас много тестов. светильники. Вместо этого вы можете добавить следующее в файл application.properties в разделе test / resources:

spring.main.allow-bean-definition-overriding=true

Вам также может понадобиться добавить его в application.properties в разделе main / resources.

Сводка

Это решение предоставляет вам точный контроль над реализациями, которые внедряются в ваш код для производства и тестирования. Решение не требует дублирования кода или внешних файлов конфигурации (кроме одного свойства в test / resources / application.properties).

...