Вы находитесь на странице: 1из 12

Шпаргалка: разрыв зависимостей и

оценка покрытия
Разрыв зависимостей
Изолированный тест не зависит от других тестов или окружения. Он работает сам по себе.
Пример. Есть два теста. Первый проверяет, что в базу данных добавилось значение. Второй —
что это значение выводится на экран.

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

Свойства изолированных тестов


Изолированные тесты можно запускать как угодно:

по одному;

в наборе тестов;

в порядке по умолчанию — том, который задан во фреймворке. Например, тесты


запускаются по алфавиту;

в случайном порядке.

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


Если тест не изолирован, это значит:

Тест падает, если запустить только его, но вместе с другими тестами проходит. Как в
примере со строками.

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

Атомарность
Хороший тест нельзя декомпозировать на несколько маленьких тестов.

Пример. Ты тестируешь интрнет-магазин. Он состоит из нескольких экранов: стартовая


страница, страница выбора товаров, личный кабинет.

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

Изоляция и атомарность

Шпаргалка: разрыв зависимостей и оценка покрытия 1


Изоляция тесно связана с атомарностью. Атомарный тест должен быть изолирован.
Пример. Кажется, что в базе данных можно объединить два теста: сначала добавить данные, а
потом вывести на экран. Но тогда тесты не атомарны.

Чтобы сделать тесты и атомарными, и изолированными, во второй тест можно передать


постоянные значения. То есть не брать их из базы, а зашить значения в сам тест.

Когда нужно разорвать зависимость


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

Нужно проверить, что будет, если внешний ресурс не сработает. Чаще всего не получится
вызвать настоящий сбой во вешней системе, но с помощью Mockito его можно сымитировать.

Как разорвать зависимость


С помощью Mockito можно разорвать зависимость тестов от внешних данных. Библиотека
сымитирует ответы внешних систем. Ты сможешь проверить, как программа ведет себя в разных
ситуациях.
Пример. Представь, что класс соединяется с удалённым сервером и получает от него данные.
Это внешняя связь. Программа зависит от сервера: если он не работает, код не получит нужную
информацию.

Класс ServiceClass получает код ответа:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class ServiceClass {

public int sendGet(String requestUrl) throws IOException {


/*
Строка преобразуется в объект URL. Его используют, чтобы установить
http/https соединение с удалённым сервером */
URL url = new URL(requestUrl);
// Открывается соединение между программой и удалённым сервером
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
/* http-метод отпраляется на сервер
GET запрашивает информацию */
connection.setRequestMethod("GET");
// Приходит код ответа от сервера
return connection.getResponseCode();
}

Класс Server проверяет, что сервер доступен:

Шпаргалка: разрыв зависимостей и оценка покрытия 2


public class Server {

public String checkServer(int responseCode) {


if (200 == responseCode) {
return "Сервер доступен";
} else {
return "Сервер недоступен";
}
}

Сервер должен обработать запрос и вернуть данные:

import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;

public class Praktikum {

@Test
public void test() throws IOException {
ServiceClass serviceClass = new ServiceClass();
Server server = new Server();
int responseCode = serviceClass.sendGet("http://www.example.com/junk");
System.out.println("Код ответа от сервера: " + responseCode);
String status = server.checkServer(responseCode);
Assert.assertEquals("Сервер доступен", status);
}

Что-то пошло не так. Метод sendGet() вернул ошибку 404 — Not Found. Тест не прошёл.
Это означает, что программа не нашла сервер. Так может получиться, если его только
разрабатывают, а тестировать нужно уже сейчас. Или если проблемы с сетью.
Чтобы протестировать код, нужно разорвать зависимость с внешним ресурсом. Поможет
библиотека Mockito: она сымитирует работу сервера. Понадобится создать мок и вернуть ответ:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.IOException;

@RunWith(MockitoJUnitRunner.class)
public class Praktikum {

// мок класса, чтобы перехватывать вызовы его методов


@Mock

Шпаргалка: разрыв зависимостей и оценка покрытия 3


ServiceClass serviceClass;

@Test
public void test() throws IOException {
Server server = new Server();
/* Вернётся код 200,
ты имитируешь корректную работу нужного ресурса */
Mockito.when(serviceClass.sendGet(Mockito.anyString())).thenReturn(200);
int responseCode = serviceClass.sendGet("http://www.example.com/junk");
System.out.println("Код ответа от сервера: " + responseCode);
String status = server.checkServer(responseCode);
Assert.assertEquals("Сервер доступен", status);
}
}

В этот раз тест получит ответ сервера.

Оценка покрытия
Покрытие кода (code coverage) говорит, какой процент программы выполняется во время тестов.
Можно смотреть на процент покрытых строк кода, условных операторов и методов.

Пример. Сервис вычисляет зарплату менеджера по продажам. Сотрудник получает 5% со всех


продаж за месяц, но не больше пятидесяти тысяч:

public class SalaryService {

public int calculateSalary(int sales) {


int percent = 5;
int salary = sales * percent / 100;
int salaryLimit = 50_000;
if (salary > salaryLimit) {
salary = salaryLimit;
}
return salary;
}
}

Тестировщик написал юнит-тест для этого сервиса:

import org.junit.Assert;
import org.junit.Test;

public class SalaryServiceTest {

@Test
public void shouldCalculateSalaryWhenUnderLimit() {
SalaryService salaryService = new SalaryService();
int actual = salaryService.calculateSalary(50_000);
int expected = 2_500;

Assert.assertEquals(expected, actual);
}
}

Шпаргалка: разрыв зависимостей и оценка покрытия 4


Нужно понять, хорошо ли метод calculateSalary покрыт тестами. Для этого понадобится оценить
покрытие.

Процент строк кода


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

Например, строка покрыта. Она всегда выполняется, когда ты


int salary = sales * percent / 100;

вызываешь метод calculateSalary . Её вызов не зависит от параметров.

А вот строка salary = salaryLimit; выполняется не всегда. Если зарплата не превышает лимит,
код внутри if не выполняется. Эта строка не покрыта.

Процент покрытых условий


Условие считается покрытым, если каждая ветвь решения выполнилась.
Например, условие if (salary > salaryLimit) покрыто частично: тест проверяет только вариант с
зарплатой ниже лимита. Превышение не проверяется.

Процент покрытых методов


Метод считается покрытым, если он выполнился при тестировании хотя бы один раз.
Например, метод calculateSalary вызывается в тесте. Значит, он покрыт.

Как считать покрытие


Вручную считать покрытие неудобно и долго. Помогают специальные инструменты. Один из
самых популярных — плагин Jacoco.
Плагин — это модуль, который расширяет возможности программы. Его нужно подключать
отдельно. Это похоже на расширения в браузере: например, блокировщик рекламы.

Подключить плагин. Нужно добавить секцию build в pom.xml . В неё и записываются плагины —
с помощью тега plugin :

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>ru.yandex.praktikum</groupId>
<artifactId>coverage</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<!--здесь настройки-->
</properties>

<dependencies>
<!--здесь зависимости-->
</dependencies>

Шпаргалка: разрыв зависимостей и оценка покрытия 5


<build>
<plugins>
<plugin>
<!--здесь плагин jacoco-->
</plugin>
</plugins>
</build>

</project>

Затем нужно указать groupId , artifactId , version — так же, как для dependency :

<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
</plugin>
</plugins>
</build>

Остаётся добавить конфигурацию для плагина — в теге executions .

Проанализировать покрытие и создать отчёт с этими данными. Понадобится две цели


Jacoco.

Цель плагина — это задача, которую он выполняет. Например, цель mvn compiler:compile —
скомпилировать код.

Первая цель — prepare-agent . Она нужна для корректной работы плагина. Вторая — report : она
генерирует отчёт.

<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<executions>
<execution>
<!--id выбираешь самостоятельно-->
<id>prepare-agent</id>
<!--в какой фазе maven будет выполняться цель-->
<phase>initialize</phase>
<!--цель jacoco, которую нужно выполнить-->
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>

Шпаргалка: разрыв зависимостей и оценка покрытия 6


</execution>
</executions>
</plugin>
</plugins>
</build>

Посмотреть отчёт. Самый простой способ посмотреть отчёт — открыть его в браузере. Выполни
команду mvn verify : для этого в IDEA нажми Ctrl дважды. Откроется окно: напиши в нём эту
команду.

Найди в папке target/site/jacoco/ файл index.html, нажми на него правой кнопкой мыши и выбери
Open In — Browser — твой браузер.

В отчёте отображается процент покрытых строк кода — столбец Missed Instructions. В примере
покрыто 90% строк.

Процент покрытых ветвей — столбец Missed Branches. В примере покрыто 50% ветвей.
Чтобы увидеть подробнее, какие именно строки и ветви не покрыты, зайди в пакет, класс и метод
внутри него:

Шпаргалка: разрыв зависимостей и оценка покрытия 7


Цвета строк означают, насколько полно тест покрыт. Зелёный — покрыто тестами, жёлтый —
покрыто частично, красный — не покрыто совсем.

Отчёт можно посмотреть и прямо в IDEA: для этого открой Run — Show Coverage Data. Нажми +
(Add) и выбери файл jacoco.exec.

Шпаргалка: разрыв зависимостей и оценка покрытия 8


После этого нажми Show Selected. У цветов слева те же значения:

Шпаргалка: разрыв зависимостей и оценка покрытия 9


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

import org.junit.Assert;
import org.junit.Test;

public class SalaryServiceTest {

@Test
public void testSalaryUnderLimit() {
SalaryService salaryService = new SalaryService();
int actual = salaryService.calculateSalary(50_000);
int expected = 2_500;

Assert.assertEquals(expected, actual);
}

@Test
public void testSalaryOverLimit() {
SalaryService salaryService = new SalaryService();
int actual = salaryService.calculateSalary(1_000_000);

Шпаргалка: разрыв зависимостей и оценка покрытия 10


int expected = 50_000;

Assert.assertEquals(expected, actual);
}
}

Теперь отчёт о покрытии сообщает о 100%:

Шпаргалка: разрыв зависимостей и оценка покрытия 11


Какой процент покрытия нужен
В реальных задачах сложно достичь покрытия 100%. Обычно на проектах выбирают другой
минимально необходимый процент покрытия — например, 80%.

Тонкости
Важно помнить: покрытие 100% не означает, что протестированы все возможные сценарии.
Инструменты оценки покрытия не учитывают, какие тестовые данные ты используешь.

Представь, что кто-то по ошибке изменил код метода, который вычисляет зарплату. Условие if

(salary > salaryLimit) превратилось в if (salary > test) :

public class SalaryService {

public int calculateSalary(int sales) {


int percent = 5;
int salary = sales * percent / 100;
int salaryLimit = 50_000;
int test = 40_000;
if (salary > test) {
salary = salaryLimit;
}
return salary;
}
}

В код закралась ошибка, а тесты всё ещё проходят и покрытие равно 100%. Кроме
количественного покрытия должно быть и качественное. Поэтому когда пишешь тесты и
подбираешь тестовые данные, важно использовать техники тест-дизайна.

Шпаргалка: разрыв зависимостей и оценка покрытия 12

Вам также может понравиться