Как изолировать компоненты, которые полагаются на другие сервисы в целях тестирования

Тестирование - очень важная часть кодирования. 📈 Разработка через тестирование - очень популярный способ тестирования, при котором вы пишете тестовый код до того, как фактически напишете какой-либо АКТУАЛЬНЫЙ код 😲.

Тест должен ИЗОЛИРОВАТЬ одну функцию одной службы и убедиться, что она работает ✅. Однако, когда эта служба зависит от других служб, может быть трудно изолировать этот один раздел и проверить, что какая-либо ошибка исходит от этой службы😞.

Вот пример того, ПОЧЕМУ нам НУЖНО издеваться

  • Допустим, мы хотим протестировать службу A, которая преобразует список строк в предложение.
  • Служба A получает этот список, вызывая службу B.
  • Служба B не работает и вместо этого возвращает null.
  • Наш тест для службы A не удастся, даже если мы правильно внедрили A.

Как нам это обойти?

МОКИНГ !!!

С помощью имитации службы B мы можем сказать нашему тесту, что возвращать при вызове службы B, вместо того, чтобы полагаться на фактическую реализацию B.

Пример использования

Мы будем тестировать службу A, которая выводит предложение «Добавлено {num1}, а {num2} результат равен {sum}». Служба A вызовет 📞 Службу B, чтобы получить сумму num1 и num2, которую нужно вставить в свое предложение.

Услуга А

Служба A (MyService) зависит от службы B (Незавершенная). Мы вызываем uninished.addNumbers (a, b). Давайте посмотрим, что мы реализовали в сервисе B прямо сейчас.

Услуга B

О нет! 🙀 похоже, что наша служба B всегда возвращает -1. так что же будет, когда мы это протестируем?

Тестирование (без насмешек) 👎

Неудивительно, что мы получаем это… 😕

org.junit.ComparisonFailure: 
Expected :Added 5 and 7 result is 12
Actual :Added 5 and 7 result is -1

Это ожидаемо, поскольку addNumbers () всегда возвращает -1. Но даже несмотря на то, что ленивый разработчик 😪 😴, отвечающий за кодирование addNumbers (), еще не закончил, МЫ не должны быть заблокированы 🚫 в нашем тестировании. Поскольку мы ЗНАЕМ, каким ДОЛЖНО БЫТЬ поведение addNumbers (), почему бы нам просто не поиздеваться над ним?

Зависимость

Чтобы высмеять это, мы будем использовать 🥁🥁🥁 MOCKITO !!

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>

добавьте эту зависимость в свой файл POM, чтобы начать работу.

Тестирование (с издевательством) 👍

В этом тесте…

  • Мы объявляем имитацию класса Unfinished в строках 11–12.
  • Затем мы запускаем макет с помощью MockitoAnnotations.initMocks (this), который запускает все объявленные макеты. И внедрите наш макет в наш экземпляр MyService (строки 18–19)
  • Наконец, волшебство происходит в строке 28. Мы сообщаем Mockito, что когда этот незаконченный экземпляр вызывает addNumbers () со значениями, хранящимися в a (5) и b ( 7), тогда он должен вернуть a + b (12).

Этот тест проходит успешно! ура!

«ПРИМЕЧАНИЕ»: если вы хотите проверить, что происходит, когда имитируемые службы не работают, вы также можете заставить их выдавать ошибки, используя .thenThrow () вместо .thenReturn ().

Когда издеваться

Мокинг следует использовать, когда у вас есть объект, в который вы вводите зависимости от других объектов через конструктор. Используя этот конструктор, мы можем внедрить наши фиктивные объекты и указать, как мы хотим, чтобы они вели себя, чтобы мы могли убедиться, что тесты, которые мы пишем, сосредоточены только на одном объекте, который мы хотим протестировать. Таким образом, НЕ ВАЖНО, если имитируемые объекты неисправны, не реализованы или не протестированы, потому что или test не будет использовать фактическую реализацию этих объектов! он будет делать то, что мы ему говорим 😃!

Если вы хотите узнать больше о mockito, вот ссылка на документацию Mockito. Там вы можете узнать больше о проверке вызовов методов и шпионаже 🕵️‍♀️ за реальными объектами, среди прочего.