Статьи

Написание сквозного теста для архитектуры микросервисов

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

Это связано с тем, что архитектура микросервисов — это распределенная система, которая затрудняет написание сквозных тестов. Предположим следующий простой пример, предоставленный Red Hat в качестве примера архитектуры микросервисов ( https://github.com/jbossdemocentral/coolstore-microservice ):

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

  • Служба Cart должна знать, как загрузить службу ценообразования, службу каталогов и MongoDB (и если вы хотите задействовать внешний интерфейс, а также Coolstore GW и WebUI ).
  • Служба корзины должна подготовить некоторые данные (данные) для обеих внешних служб.
  • Вы общаетесь со службами, используя сеть. Может случиться, что некоторые тесты не пройдут не из-за реального сбоя, а из-за проблемы инфраструктуры или из-за ошибки других служб. Таким образом, вероятность того, что эти тесты станут ненадежными и начнут давать сбои, не потому, что любые изменения, внесенные в текущую службу, выше.
  • В более сложных случаях выполнение этих тестов может быть дорогим с точки зрения стоимости (развертывание в облаке), времени (загрузка всей инфраструктуры и сервисов) и времени обслуживания.
  • Сложно запустить их на компьютере разработчика, так как вам нужны все компоненты, установленные на компьютере.

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

Необходимо найти способ «симулировать» эти внешние зависимости, не вводя какой-либо фиктивный объект. Нам нужно обмануть тестируемый сервис, чтобы он действительно думал, что он взаимодействует с реальными внешними сервисами, а на самом деле это не так.

Метод, который позволяет нам это сделать, — это Service Virtualiztion. Виртуализация служб — это метод, позволяющий имитировать поведение компонентов приложений, таких как API.

Вы можете думать о виртуализации сервисов как о подходе, который вы использовали для реализации в ООП, но вместо симуляции на уровне объекта вы симулируете на уровне сервиса. Это издевательство над предприятием.

Существует множество инструментов виртуализации сервисов, но, по моему опыту, в экосистеме JVM одним из лучших инструментов является Hoverfly .

Давайте посмотрим, как выглядит «сквозной» тест для Cart Service.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    properties = "CATALOG_ENDPOINT=catalog")
public class CartServiceBoundaryTest {
 
    @Autowired
    private TestRestTemplate restTemplate;
 
    @ClassRule
    public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(dsl(
        service("catalog")
            .get("/api/products")
            .willReturn(success(json(ProductsObjectMother.createVehicleProducts())))
    ));
 
    @Test
    public void should_add_item_to_shopping_cart() {
 
        final ShoppingCart shoppingCart = this.restTemplate.postForObject("/api/cart/1/1111/2", "", ShoppingCart.class);
 
        assertThat(shoppingCart)
            .returns(0.0, ShoppingCart::getCartItemPromoSavings)
            .returns(2000.0, ShoppingCart::getCartItemTotal)
            .returns(-10.99, ShoppingCart::getShippingPromoSavings)
            .returns(2000.0, ShoppingCart::getCartTotal)
            .extracting(ShoppingCart::getShoppingCartItemList)
            .hasSize(1);
    }
}

Этот сервис реализован с использованием Spring Boot , поэтому мы используем среду Spring Boot Test. Важной частью здесь является то, что URL, на котором развернута служба каталогов, указывается с помощью
CATALOG_ENDPOINT свойство. И для этого теста он установлен в каталог .

Следующим важным моментом является раздел правил класса Hoverfly. В этом правиле указаны следующие вещи:

  1. Прокси-сервер Http запускается перед тестированием, и весь исходящий трафик из JVM перенаправляется на этот прокси-сервер.
  2. Он записывает, что когда запрос к каталогу хоста выполнен и путь к нему равен / api / products, он должен вернуть результат успеха с данным документом json.

Сам тест просто использует TestRestTemplate (это клиент отдыха) и проверяет, что вы можете добавить некоторые элементы в корзину.

Обратите внимание, что вам не нужно настраивать, где запущен прокси-сервер Http, или настраивать любой порт, потому что Hoverfly автоматически настраивает сетевые параметры JVM, поэтому любой сетевой обмен данными осуществляется через прокси-сервер Hoverfly.

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

Вы тестируете весь сервис в его границах, от входящих сообщений до исходящих сообщений другим сервисам, без насмешек над любым внутренним элементом.

Возможно, вы задаетесь вопросом: «Что происходит в случае, если текущий сервис также зависит от сервера базы данных?»

В этом случае вы делаете это как обычно, поскольку сама служба знает, какой сервер баз данных использует и какой тип данных ему требуется, вам нужно только загрузить сервер базы данных, заполнить необходимые данные (фикстуры) и выполнить тесты. Для этого сценария я предлагаю вам использовать Arquillian Cube Docker для загрузки службы базы данных из контейнера Docker, чтобы вам не нужно было устанавливать ее на каждом компьютере, для которого нужно запустить тесты, и Arquillian Persistence Extension для поддержания базы данных в известном состоянии.

В следующем примере сервиса оценки вы можете кратко увидеть, как использовать их для тестов на постоянство:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ApueCubeRatingServiceTest {
  // Starts in local dockerhost (docker machine or native) mongo docker image before running the test class
  @ClassRule
  public static ContainerDslRule mongodbContainer = new ContainerDslRule("mongo:3.2.18-jessie")
      .withPortBinding(27017);
 
  //Defines APE (Arquillian Persistence Extension to work as rule)
  @Rule
  public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule();
 
  // Defines to use MongoDb as NoSql Populator
  @MongoDb
  @ArquillianResource
  NoSqlPopulator populator;
 
  @Test
  public void should_calculate_average_rating_when_adding_an_already_inserted_item() {
 
    createPopulatorConfiguration()
                .usingDataSet("single_rating_with_double.json")
                .execute();
     
    // Execute test
     
  }
   
  @After
  public void tearDown() {
    createPopulatorConfiguration().clean();
  }
   
  private NoSqlPopulatorConfigurator createPopulatorConfiguration() {
          return populator.forServer(
              mongodbContainer.getIpAddress(),
              mongodbContainer.getBindPort(27017))
              .withStorage(TEST_DATABASE);
  }
}

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

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

Как контрактные тесты подходят для этого? На самом деле, все, что показано здесь, может быть использовано на стороне клиента и поставщика при тестировании контракта, чтобы избежать необходимости загружать какие-либо внешние сервисы. Таким образом, как утверждают многие авторы, если вы используете контрактные тесты, они становятся новым сквозным тестом.

Вы можете увидеть полные проекты, где оба теста используются здесь и здесь.

Мы продолжаем учиться,

Опубликовано на Java Code Geeks с разрешения Алекса Сото, партнера нашей программы JCG . См. Оригинальную статью здесь: Написание сквозного теста для архитектуры микросервисов

Мнения, высказанные участниками Java Code Geeks, являются их собственными.