Полный учебник по JUnit 5 Mockito для модульного тестирования

Mockito — это фреймворк модульного тестирования для Java, который упрощает задачу автоматизации тестирования. Благодаря инъекции зависимостей и проверкам во время компиляции он делает модульное тестирование высокоэффективным и чистым. Кроме того, Mockito помогает повысить тестовую независимость разрабатываемых компонентов, помогая создавать объекты, не зависящие от конфигурации, специфичной для конкретного теста. Наиболее популярный способ использования Mockito — в рамках фреймворка JUnit, чтобы помочь вам писать более качественные тесты.

Для начала, мокинг в модульном тестировании используется для изоляции AUT (тестируемого приложения) от внешних зависимостей. Мокинг необходимо использовать, когда реализация внешних зависимостей еще не завершена.

В этом руководстве по JUnit 5 Mockito мы рассмотрим, как Mockito можно использовать вместе с фреймворком для модульного тестирования JUnit для мокинга в JUnit 5.

Давайте приступим!

Начало работы с Mockito и JUnit 5

Mockito — это фреймворк автоматизации тестирования с открытым исходным кодом, который использует Java Reflection API для создания mock-объектов. Макетные объекты — это фиктивные объекты, используемые для реальной реализации. Основная цель использования фиктивных объектов — упростить разработку теста путем имитации внешних зависимостей и использования их в коде.

Согласно моему опыту, в модульных тестах следует использовать объекты-макеты, когда реальный объект имеет недетерминированное поведение, или реальный объект является функцией обратного вызова, или реальный объект еще не реализован.

В этом руководстве по JUnit 5 Mockito мы будем использовать Mockito вместе с JUnit для выполнения автоматизированного тестирования Selenium. Если вы только начинаете работать с JUnit, посмотрите эту статью о том, как настроить среду JUnit для первого тестирования. Однако вы также можете просмотреть учебник Selenium JUnit, чтобы ознакомиться с основами JUnit, от написания первого теста до запуска тестов в Selenium с примерами.

Установка Mockito

Когда вы используете Mockito в своих модульных тестах, вам нужно скачать jar-файл и поместить его в путь, который сможет найти ваша система сборки. Mockito доступен в двух версиях: mockito-core (которая содержит только ядро Mockito, и mockito-all (которая содержит все модули).

Предпочтительный способ установки Mockito — объявить зависимость от mockito-core в выбранной системе сборки. Второй лучший способ — загрузить артефакты вручную и добавить их в classpath. Вы также можете добавить зависимости в существующий проект Maven или Gradle.

Добавьте следующие зависимости в ваш pom.xml:

    <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.1.0</version> <scope>test</scope> </dependency>
Войти в полноэкранный режим Выход из полноэкранного режима

При использовании Gradle этого можно добиться, объявив следующую зависимость в build.gradle:

  compile("org.mockito:mockito-core:4.1.0")
Ввести полноэкранный режим Выйти из полноэкранного режима

Последняя версия Mockito на момент написания этой статьи — 4.1.0.

Аннотации в Mockito

Как и аннотации JUnit, аннотации Mockito используются для указания поведения тестового кода. Это позволяет пользователям больше сосредоточиться на своей логике и при этом очень эффективно тестировать код. Этот раздел руководства JUnit 5 Mockito в основном посвящен аннотациям Mockito и тому, как использовать их в Selenium.

1. Аннотация @Mock в Mockito

Аннотация Mock используется для создания имитационного объекта.

    [@Mock](http://twitter.com/Mock)
    ToDoService serviceMock;
Вход в полноэкранный режим Выход из полноэкранного режима

Аннотация @Mock всегда используется вместе с @RunWith, аннотацией на уровне класса. Мы подробно рассмотрим, как обе аннотации используются для создания и использования объекта-макета в следующем сегменте.

Пока же рассмотрим следующий синтаксический пример:

    [@RunWith](http://twitter.com/RunWith)(MockitoJUnitRunner.class) 
    public class DemoMock { 
    ..... 
    }
Вход в полноэкранный режим Выйти из полноэкранного режима

2. Аннотация @spy в Mockito

Аннотация Spy используется для создания реального объекта и слежки за ним. Это поможет вызывать все методы объекта и при этом отслеживать каждое взаимодействие, которое имитируется.

Ниже приведена простая реализация аннотации @spy:

    [@Spy](http://twitter.com/Spy)
    List<String> myList = new ArrayList<String>();

    [@Test](http://twitter.com/Test)
    public void usingSpyAnnotation() {
       myList.add("Hello, This is LambdaTest");
       Mockito.verify(spyList).add("Hello, This is LambdaTest");
       assertEquals(1, spyList.size());
    }
Войти в полноэкранный режим Выход из полноэкранного режима

3. Аннотация @Captor в Mockito

Аннотация Captor используется для создания экземпляра ArgumentCaptor для захвата значений аргументов метода для дальнейших утверждений.

Вот простая реализация аннотации @Captor, которая захватывает ключ и значения MyMap:

    [@Mock](http://twitter.com/Mock)
    HashMap<String, Integer> MyMap;
    [@Captor](http://twitter.com/Captor)
    ArgumentCaptor<String> keyCaptor;
    [@Captor](http://twitter.com/Captor)
    ArgumentCaptor<Integer> valueCaptor;
    [@Test](http://twitter.com/Test)
    public void ArgumentCaptorTest()
    {
       hashMap.put("A", 10);
       Mockito.verify(MyMap).put(keyCaptor.capture(), valueCaptor.capture());
       assertEquals("A", keyCaptor.getValue());
       assertEquals(new Integer(10), valueCaptor.getValue());
    }
Вход в полноэкранный режим Выход из полноэкранного режима

4. Аннотация @InjectMocks в Mockito

Аннотация InjectMocks используется для подражания классу со всеми его зависимостями. Это очень полезно для полного тестирования поведения.

В примере ниже мы будем использовать @InjectMock Countries в Continent:

    [@Mock](http://twitter.com/Mock)
    Map<String, String> Countries;

    [@InjectMocks](http://twitter.com/InjectMocks)
    MyDictionary dic = new Continent();

    [@Test](http://twitter.com/Test)
    public void UseInjectMocksAnnotation() {
       Mockito.when(Countries.get("India")).thenReturn("asia");

       assertEquals("asia", dic.getContinent("India"));
    }
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь, когда мы познакомились с различными аннотациями Mockito, давайте углубимся в создание Mocks во фреймворке Mockito.

Однако, если вы хотите узнать больше об аннотациях JUnit в Selenium, вы можете просмотреть следующее видео с канала LambdaTest на YouTube и оставаться в курсе других подобных видео об учебнике по JUnit с Selenium, Selenium тестировании, Cypress тестировании и многом другом.

Как создать Mocks в Mockito?

В этом разделе учебника JUnit 5 Mockito мы рассмотрим различные способы создания Mocks во фреймворке Mockito. В Mockito мы можем создавать объекты mock двумя способами:

  1. Используя аннотацию @Mock

  2. Используя метод Mock()

Использование аннотации @Mock для создания моков

Аннотация @Mock минимизирует повторяющиеся mock-объекты и делает более читаемым тестовый код и ошибки проверки. Она доступна в пакете org.mockito. Вот фрагмент кода с аннотацией @mock:

    [@Mock](http://twitter.com/Mock)
    ToDoService serviceMock;
Вход в полноэкранный режим Выход из полноэкранного режима

Использование метода @Mock для создания моков

Метод Mockito.mock() позволяет нам создать объект-макет классов и интерфейсов. Ниже приведен фрагмент кода для простого макета класса:

    MyList listMock = mock(MyList.class);
    when(listMock.add(anyString())).thenReturn(false);
Вход в полноэкранный режим Выход из полноэкранного режима

В приведенном выше фрагменте кода MyList — это класс, для которого мы создали объект-макет, передав его в качестве параметра в методе mock(). Вторая строка задает ожидание. Когда вызывается метод add() класса MyList, он должен вернуть false.

Чтобы понять это более четко, давайте рассмотрим пример приложения электронной коммерции. Предположим сценарий, в котором вам нужно протестировать функцию оформления заказа. Есть класс Order, в котором есть метод checkout(), завершающий оформление заказа и обрабатывающий платеж. Для простоты мы намеренно не учитываем другие условия.

Таким образом, класс Order будет выглядеть примерно так:

    public class Order {

           public Order() {
           }

           public String checkout(PaymentProviderService payment) {
               try {
                   return payment.processPayment().equalsIgnoreCase("Approved") ? "Success" : "Failure";
               }
               catch(Exception e) {
                   return "Exception occurred at payment provider service when trying to checkout";
               }
           }
       }
Войти в полноэкранный режим Выйти из полноэкранного режима

В приведенном выше примере метод checkout() использует класс PaymentProviderService для обработки платежей. Успех или неудача оформления заказа основывается на методе processPayment().

Поэтому если мы хотим написать тест-кейсы для функциональности checkout, нам также необходимо создать экземпляр класса PaymentProviderService. Но мы не хотим зависеть от этого класса, чтобы начать писать наши модульные тесты. В этом случае мы создадим объект-макет класса PaymentProviderService и продолжим писать тесты для класса Order.

Вот фрагмент кода:

    public class OrderTests {

           [@Test](http://twitter.com/Test)
           public void checkoutOrderSuccessOnPaymentSuccess() {

               Order order = new Order();
               PaymentService ppMock = mock(PaymentService.class);
               when(ppMock.processPayment()).thenReturn("Approved");

               Assert.assertEquals(
                   "Success",
                   order.checkout(ppMock)
               );
           }
Войти в полноэкранный режим Выход из полноэкранного режима

Описание кода

В приведенном выше коде я создал тестовый пример для класса Order, который проверяет сценарий успешного размещения заказа.

    PaymentProviderService ppMock = mock(PaymentProviderService.class);
Вход в полноэкранный режим Выйти из полноэкранного режима

Приведенная выше строка создает объект-макет класса PaymentProviderService. Теперь мы можем использовать этот объект для вызова метода processPayment(). Мы можем написать ожидаемое поведение, используя when…thenReturn as:

 when(ppMock.processPayment()).thenReturn("Approved");

Войти в полноэкранный режим Выйти из полноэкранного режима

После этого мы можем подтвердить ожидаемые и фактические выходы и выполнить желаемую функцию. Аналогичным образом мы создали 4 модульных теста для класса Order. Результат выполнения этих тестов в IDE показан ниже:

Вы используете Playwright для автоматизации тестирования? Запускайте свои тестовые сценарии Playwright мгновенно на 50+ комбинациях браузеров/OS с помощью облака LambdaTest. Зарегистрируйтесь бесплатно!

Как использовать Mockito с расширениями JUnit 5?

Этот раздел учебника JUnit 5 Mockito посвящен использованию Mockito с JUnit 5. В нем показано, как добавить объект-макет в тестовый пример и проверить его поведение с помощью JUnit 5. JUnit 5 имеет модель расширения, которая поддерживает Mockito «из коробки». Модель расширения JUnit 5 позволяет пользователям предоставлять пользовательские аннотации и поведение для тестовых дублеров.

Для начала необходимо добавить зависимости JUnit Jupiter, помимо зависимостей mockito-core. Поэтому в pom.xml (для Maven) должны быть добавлены следующие зависимости:

    <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-junit-jupiter</artifactId>
       <version>4.1.0</version>
       <scope>test</scope>
    </dependency>
Войти в полноэкранный режим Выход из полноэкранного режима

Вот как можно добавить зависимости для Gradle:

    dependencies{
    testImplementation('org.mockito:mockito-core:4.1.0')

The extension eliminates the need for the MockitoAnnotations.openMocks() method call. So the above example can be modified as:

    [@ExtendWith](http://twitter.com/ExtendWith)(MockitoExtension.class)
    public class OrderTestsUsingExtension {

               [@Test](http://twitter.com/Test)
               public void checkoutOrderSuccessOnPaymentSuccess() {

                   Order order = new Order();
                   PaymentProviderService ppMock = mock(PaymentProviderService.class);
                   when(ppMock.processPayment()).thenReturn("Approved");

                   Assert.assertEquals(
                       "Success",
                       order.checkout(ppMock)
                   );
               }
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Все просто, не так ли? Это делает наш код более читабельным и чистым. Кроме того, это позволяет обнаружить некорректные заглушки.

В следующем разделе этого учебника JUnit 5 Mockito будет показано параллельное тестирование в Selenium с использованием Mockito и JUnit.

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

Вот краткий обзор сертификации JUnit от LambdaTest:

Как проводить параллельное тестирование с помощью Mockito и JUnit 5 на облачной Selenium Grid

В JUnit 5 появилась долгожданная возможность параллельного выполнения тестов, что значительно сокращает время выполнения. Это просто спасение для тестировщиков, поскольку в реальном мире у нас огромное количество тестовых случаев. Использование этой возможности с помощью онлайн Selenium Grid, например LambdaTest, будет более эффективным.

Инструменты тестирования Selenium, такие как LambdaTest, позволяют проводить кросс-браузерное тестирование на более чем 3000 онлайн браузеров и комбинаций операционных систем.

Вот краткий видеоурок по браузерному тестированию в реальном времени.

В этом разделе учебника JUnit 5 Mockito мы рассмотрим простой пример для демонстрации параллельного тестирования с помощью Mockito и JUnit 5:

Постановка задачи

  1. Перейдите на сайт https://www.lambdatest.com.

  2. Убедитесь, что заголовок страницы — «Самый мощный инструмент кросс-браузерного тестирования | LambdaTest».

  3. Введите имя пользователя и пароль.

  4. Нажмите на кнопку входа в систему.

  5. Подтвердите успешный вход сообщением «Welcome — LambdaTest».

Мы будем выполнять этот тестовый пример на следующих браузерах, версиях и комбинациях платформ, используя LambdaTest remote Selenium Grid:

Браузер Версия Платформа
Chrome 70.0 WIN10
Safari 14.0.2 macOS Big Sur
Firefox 76.0 WIN10
package MockitoDemo.MockitoDemo;
    import org.junit.jupiter.api.AfterAll;
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.parallel.Execution;
    import org.junit.jupiter.api.parallel.ExecutionMode;
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.Arguments;
    import org.junit.jupiter.params.provider.MethodSource;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.remote.DesiredCapabilities;
    import org.openqa.selenium.remote.RemoteWebDriver;
    import org.openqa.selenium.support.ui.ExpectedConditions;
    import org.openqa.selenium.support.ui.WebDriverWait;
    import java.util.stream.Stream;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.concurrent.TimeUnit;
    import static org.junit.jupiter.params.provider.Arguments.arguments;
    @Execution(ExecutionMode.CONCURRENT)
    public class CrossbrowserDemo {
    String username = "YOUR_USER_NAME";
           String accesskey = "YOUR_ACCESS_KEY";

          static RemoteWebDriver driver = null;
          String gridURL = "@hub.lambdatest.com/wd/hub";
          String urlToTest = "https://www.lambdatest.com/";

          @BeforeAll
          public static void start() {
              System.out.println("=======Starting junit 5 tests in LambdaTest  Grid========");
          }

          @BeforeEach
          public void setup(){
              System.out.println("=======Setting up drivers and browser========");
          }

          public void browser_setup(String browser) {
              System.out.println("Setting up the drivers and browsers");
              DesiredCapabilities capabilities = new DesiredCapabilities();

              if(browser.equalsIgnoreCase("Chrome")) {
                  capabilities.setCapability("browserName", "chrome");    //To specify the browser
                  capabilities.setCapability("version", "70.0");        //To specify the browser version
                  capabilities.setCapability("platform", "win10");        // To specify the OS
                  capabilities.setCapability("build", "Running_Junit5Tests_In_Grid_Chrome");               //To identify the test
                  capabilities.setCapability("name", "JUnit5Tests_Chrome");
                  capabilities.setCapability("network", true);        // To enable network logs
                  capabilities.setCapability("visual", true);            // To enable step by step screenshot
                  capabilities.setCapability("video", true);            // To enable video recording
                  capabilities.setCapability("console", true);            // To capture console logs
              }
              if(browser.equalsIgnoreCase("Firefox")) {
                  capabilities.setCapability("browserName", "Firefox");  //To specify the browser
                  capabilities.setCapability("version", "76.0");    //To specify the browser version
                  capabilities.setCapability("platform", "win10");   // To specify the OS
                  capabilities.setCapability("build", "Running_Junit5Tests_In_Grid_Firefox");    //To identify the test
                  capabilities.setCapability("name", "JUnit5Tests_Firefox");
                  capabilities.setCapability("network", true);      // To enable network logs
                  capabilities.setCapability("visual", true);       // To enable step by step screenshot
                  capabilities.setCapability("video", true);        // To enable video recording
                  capabilities.setCapability("console", true);      // To capture console logs

              }
              if(browser.equalsIgnoreCase("Safari")) {
                  capabilities.setCapability("browserName", "Safari");  //To specify the browser
                  capabilities.setCapability("version", "14.0.2");    //To specify the browser version
                  capabilities.setCapability("platform", "macOS Big Sur");   // To specify the OS
                  capabilities.setCapability("build", "Running_Junit5Tests_In_Grid_Safari");    //To identify the test
                  capabilities.setCapability("name", "JUnit5Tests_Firefox");
                  capabilities.setCapability("network", true);      // To enable network logs
                  capabilities.setCapability("visual", true);       // To enable step by step screenshot
                  capabilities.setCapability("video", true);        // To enable video recording
                  capabilities.setCapability("console", true);      // To capture console logs

              }
              try {
                  driver = new RemoteWebDriver(new URL("https://" + username + ":" + accesskey + gridURL), capabilities);
              } catch (MalformedURLException e) {
                  System.out.println("Invalid grid URL");
              } catch (Exception e) {
                  System.out.println(e.getMessage());
              }

          }

          @ParameterizedTest
          @MethodSource("browser")
          public void launchAndVerifyTitle_Test(String browser) {
              browser_setup(browser);
              String methodName = Thread.currentThread()
                      .getStackTrace()[1]
                      .getMethodName();
              driver.get(urlToTest);
              driver.manage().window().maximize();
              driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
              String actualTitle = driver.getTitle();
              System.out.println("The page title is "+actualTitle);
              String expectedTitle ="Most Powerful Cross Browser Testing Tool Online | LambdaTest";
              System.out.println("Verifying the title of the webpage started");
              Assertions.assertEquals(expectedTitle, actualTitle);
              System.out.println("The webpage has been launched and the title of the webpage has been veriified successfully");
              System.out.println("********Execution of "+methodName+" has ended********");
          }

          @ParameterizedTest
          @MethodSource("browser")
          public void login_Test(String browser) {
              browser_setup(browser);

              String methodName = Thread.currentThread()
                      .getStackTrace()[1]
                      .getMethodName();
              driver.get(urlToTest);
              driver.manage().window().maximize();
              driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
              WebElement login = driver.findElement(By.xpath("//a[text()='Login']"));
              login.click();
              WebElement username = driver.findElement(By.xpath("//input[@name='email']"));
              WebElement password = driver.findElement(By.xpath("//input[@name='password']"));
              WebDriverWait wait = new WebDriverWait(driver,20);
              wait.until(ExpectedConditions.visibilityOf(username));
              username.clear();
              username.sendKeys("ruchirashukla89@gmail.com");
              password.clear();
              password.sendKeys("ruchira@89");
              WebElement loginButton = driver.findElement(By.xpath("//button[text()='Login']"));
              loginButton.click();
              driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
              String actual = driver.getTitle();
              String expected = "Welcome - LambdaTest";
              Assertions.assertEquals(expected, actual);
              System.out.println("The user has been successfully logged in");
              System.out.println("********Execution of "+methodName+" has ended********");
          }

          @ParameterizedTest
          @MethodSource("browser")
          public void logo_Test(String browser) {
              browser_setup(browser);
              String methodName = Thread.currentThread()
                      .getStackTrace()[1]
                      .getMethodName();
              System.out.println("********Execution of "+methodName+" has been started********");
              System.out.println("Launching LambdaTest website started..");
              driver.get(urlToTest);
              driver.manage().window().maximize();
              driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
              System.out.println("Verifying of webpage logo started..");

              WebElement logo = driver.findElement(By.xpath("//*[@id="header"]/nav/div/div/div[1]/div/a/img"));
              boolean is_logo_present = logo.isDisplayed();
              if(is_logo_present) {
                  System.out.println("The logo of LambdaTest is displayed");
              }
              else {
                  Assertions.assertFalse(is_logo_present,"Logo is not present");
              }
              System.out.println("********Execution of "+methodName+" has ended********");
          }

          @AfterEach
          public void tearDown() {
              System.out.println("Quitting the browsers has started");
              driver.quit();
              System.out.println("Quitting the browsers has ended");
          }

          @AfterAll
          public static void end() {
              System.out.println("Tests ended");
          }

          static Stream<Arguments> browser() {
              return Stream.of(
                      arguments("Chrome"),
                      arguments("Firefox"),
                      arguments("Safari")
              );
          }

    }
Вход в полноэкранный режим Выйти из полноэкранного режима

Описание кода

В приведенном выше коде три теста будут выполняться параллельно на указанных комбинациях ОС браузера.

Аннотация @execution(ExecutionMode.CONCURRENT) определяет параллельный режим в JUnit 5. Удаленный Selenium Grid LambdaTest инициирует выполнение на основе аргументов, определенных в возможностях.

Аннотация @ParameterizedTest определяет, что аннотируемый метод параметризован, а аннотация @MethodSource используется для указания имени метода, который используется в качестве источника значений параметров.

Наконец, метод browser(), который является источником метода, имеет имена браузеров в качестве аргументов.

После выполнения мы получим журналы, как показано ниже:

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

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

Итак, если вам интересно узнать больше о выполнении параллельного тестирования с помощью JUnit 5 и Selenium, это видео поможет вам начать. Это видео проведет вас через различные параллельные параметры в тестировании JUnit, включая методы — выполнение методов теста в отдельных потоках и классы — выполнение классов теста в отдельных потоках.

Тестирование на Selenium Automation Testing Grid Cloud из 3000+ настольных и мобильных браузеров.

Заключение

В этом руководстве по JUnit 5 Mockito мы рассмотрели основы мокинга и фреймворка Mockito. Также мы узнали, что такое различные аннотации в Mockito и их применение. В JUnit 5 создание макетов с помощью Mockito стало более простым за счет расширения. Мы также обсудили параллельное выполнение тестовых примеров в JUnit5 с использованием LambdaTest cloud Selenium Grid. Хотя Mockito широко используется в модульном тестировании, мы также можем использовать его широкие возможности для интеграции этих модульных тестов в автоматизированное тестирование Selenium.

Как вы использовали Mockito в своем тестировании? Расскажите нам об этом в комментариях ниже.

Счастливого тестирования!

Оцените статью
devanswers.ru
Добавить комментарий