410013796724260
• Webmoney
R335386147728
Z369087728698
JUnit и фреймворк MockitoРазработчикам программного обеспечения на разных этапах своей деятельности приходится сталкиваться с тремя стратегиями тестирования : функциональным, интеграционным и модульным тестированием. Все они используются для тестирования приложений разными способами, и каждая из стратегий имеет определенную цель. К сожалению, ни одна из стратегий не даёт стопроцентной гарантии обнаружения всех имеющихся ошибок. И даже комбинация всех трёх стратегий не может дать такой гарантии. Но их сочетание позволяет существенно снизить количество ошибок и убедить разработчика, что приложение функционирует согласно предъявленным требованиям. Функциональное тестированиеПроведение функционального тестирования, как правило, связано с созданием специальной группы специалистов, занимающихся тестированием. На этом этапе приложение развертывается в действующем окружении и проверяется его соответствие ТЗ (Техническому Заданию) и предъявленным функциональным требованиям. Команда тестеровщиков использует комплекс автоматизированных и ручных тестов. Автоматизировать процесс функционального тестирования можно, если приложение включает API (Application Programming Interface) - интерфейс прикладного программирования, на котором оно построено. Однако наличие интерфейса в приложении (desktop, web) существенно снижает возможности полной автоматизации данного процесса. Интеграционное тестированиеСтратегия интеграционного тестирования основывается на проверке прикладного кода в окружении, близком к фактическому окружению, но не являющимся им. Главная цель данной стратегии – убедиться в правильности взаимодействия кода с внешними ресурсами и взаимодействия различных технологий в приложении между собой. В интеграционном тестировании не требуется использовать фиктивные данные, как при модульном тестировании. Вместо этого в интеграционных тестах часто используются базы данных, находящиеся в памяти, которые легко можно создавать и уничтожать во время выполнения тестов. База данных в памяти – это самая настоящая база данных, что дает возможность проверить правильность работы сущностей JPA. Но все же эта база данных не совсем настоящая – она лишь имитирует настоящую базу данных для целей интеграционного тестирования. Модульное тестированиеЦелью модульного тестирования является проверка работы прикладной логики всего приложения или отдельных его частей при разных исходных данных, и анализ правильности получаемых результатов. Несмотря на то, что цель модульного тестирования выглядит простой и понятной, реализация этого типа тестирования может оказаться очень сложным и запутанным делом, особенно при наличии «старого» кода. Основные приемы проведения модульного тестирования опираются на следующие базовые принципы :
При проведении модульного тестирования для создания фиктивных классов-зависимостей можно использовать простой, но мощный фреймворк Mockito совместно с JUnit. Фреймворк MockitoФреймворк Mockito предоставляет ряд возможностей для создания заглушек вместо реальных классов или интерфейсов при написании JUnit тестов. Mockito можно скачать с сайта https://code.google.com/p/mockito, либо определить в зависимостях (dependencies) в maven проекте :
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
Наибольшее распространение получили следующие возможности Mockito :
Синтаксис создания заглушки MockitoЧтобы создать Mockito объект можно использовать либо аннотацию @Mock, либо метод mock. В следующем примере в двух разных классах (Test_Mockito1, Test_Mockito2) разными способами создаются объекты mcalc для имитации интерфейса калькулятора ICalculator.
import org.mockito.Mock;
import org.mockito.Mockito;
import com.example ICalculator;
public class Test_Mockito1
{
@Mock
ICalculator mcalc;
}
-----------------------------------------------------------
public class Test_Mockito2
{
ICalculator mcalc = Mockito.mock(ICalculator.class);
}
Если использовать статический импорт Mockito, то синтаксис будет иметь следующий вид :
import static org.mockito.Mockito.*;
import com.example ICalculator;
public class Test_Mockito
{
ICalculator mcalc = mock(ICalculator.class);
}
ПРИМЕЧАНИЕ : помните, что методы mock объекта возвращают значения по умолчанию : false для boolean, 0 для int, пустые коллекции, null для остальных объектов. Методы Mockito в примерахДля рассмотрения методов фреймворка Mockito будем использовать в качестве тестового класса калькулятор, реализующий интерфейс ICalculator. В следующем коде представлены листинги интерфейса ICalculator и класса Calculator. Листинги тестового класса и интерфейса
public interface ICalculator
{
public double add (double d1, double d2);
public double subtract (double d1, double d2);
public double multiply (double d1, double d2);
public double divide (double d1, double d2);
}
//---------------------------------------------------
public class Calculator
{
ICalculator icalc;
public Calculator(ICalculator icalc) {
this.icalc = icalc;
}
public double add(double d1, double d2) {
return icalc.add(d1, d2);
}
public double subtract(double d1, double d2) {
return icalc.subtract(d1, d2);
}
public double multiply(double d1, double d2) {
return icalc.multiply(d1, d2);
}
public double divide(double d1, double d2) {
return icalc.divide(d1, d2);
}
public double double15() {
return 15.0;
}
}
Как видно из листингов все методы калькулятора (add, subtract, multiply, divide), за исключением одного, возвращают не вычисленные значения, а результаты выполнения данных методов в объекте, реализующим интерфейс ICalculator, который в наших тестах будет представлять заглушка mcalc. Последний метод double15() должен вернуть реальное значение. 1. Определение поведения - when(mock).thenReturn(value)Этот метод позволяет определить возвращаемое значение при вызове метода mock с заданными параметрами. Если будет указано более одного возвращаемого значения, то они будут возвращены методом последовательно, пока не вернётся последнее; после этого при последующих вызовах будет возвращаться только последнее значение. Таким образом, чтобы метод всегда возвращал одно и то же значение, седует просто определить одно условие.
@RunWith(MockitoJUnitRunner.class)
public class Test_Mockito
{
@Mock
ICalculator mcalc;
// используем аанотацию @InjectMocks для создания mock объекта
@InjectMocks
Calculator calc = new Calculator(mcalc);
@Test
public void testCalcAdd()
{
// определяем поведение калькулятора для операции сложения
when(calc.add(10.0, 20.0)).thenReturn(30.0);
// проверяем действие сложения
assertEquals(calc.add(10, 20), 30.0, 0);
// проверяем выполнение действия
verify(mcalc).add(10.0, 20.0);
// определение поведения с использованием doReturn
doReturn(15.0).when(mcalc).add(10.0, 5.0);
// проверяем действие сложения
assertEquals(calc.add(10.0, 5.0), 15.0, 0);
verify(mcalc).add(10.0, 5.0);
}
}
Метод verify позволяет проверить, была ли выполнена проверка с определенными параметрами. Если проверка не выполнялась или выполнялась с другими параметрами, то verify вызовет исключение. Для определения поведения mock в тесте была использована также следующая конструкция : doReturn(value).when(mock).method(params) 2. Подсчет количества вызовов - atLeast, atLeastOnce, atMost, times, neverДля проверки количества вызовов определенных методов Mockito предоставляет следующие методы :
Следующий тест демонстрирует контроль количества вызовов метода subtract с разными параметрами. Для этого сначала определяется поведение mock (при определенных параметрах выдавать соответствующие результаты), и выполняются проверки с использованием assertEquals. После этого выполняется проверка количества вызовов mock с определенными параметрами. Две последние проверки не выполняются - «закомментированы». Если снять комментарий хотя бы с одной из них, то метод verify, вызовет исключение. Комментарий к этим проверкам описывает причину вызова методом исключения.
@Test
public void testCallMethod()
{
// определяем поведение (результаты)
when(mcalc.subtract(15.0, 25.0)).thenReturn(10.0);
when(mcalc.subtract(35.0, 25.0)).thenReturn(-10.0);
// вызов метода subtract и проверка результата
assertEquals (calc.subtract(15.0, 25.0), 10, 0);
assertEquals (calc.subtract(15.0, 25.0), 10, 0);
assertEquals (calc.subtract(35.0, 25.0),-10, 0);
// проверка вызова методов
verify(mcalc, atLeastOnce()).subtract(35.0, 25.0);
verify(mcalc, atLeast (2)).subtract(15.0, 25.0);
// проверка - был ли метод вызван 2 раза?
verify(mcalc, times(2)).subtract(15.0, 25.0);
// проверка - метод не был вызван ни разу
verify(mcalc, never()).divide(10.0,20.0);
/* Если снять комментарий со следующей проверки, то
* ожидается exception, поскольку метод "subtract"
* с параметрами (35.0, 25.0) был вызван 1 раз
*/
// verify(mcalc, atLeast (2)).subtract(35.0, 25.0);
/* Если снять комментарий со следующей проверки, то
* ожидается exception, поскольку метод "subtract"
* с параметрами (15.0, 25.0) был вызван 2 раза, а
* ожидался всего один вызов
*/
// verify(mcalc, atMost (1)).subtract(15.0, 25.0);
}
3. Обработка исключений - when(mock).thenThrow()Mockito позволяет вызвать исключение при определенных условиях. Для этого необходимо использовать следующий синтаксис кода :
// создаем исключение
RuntimeException exception = new RuntimeException ("Division by zero");
// определение поведения для вызова исключения
doThrow(exception).when(mock).divide(5.0, 0));
В представленном коде создали исключение RuntimeException. После этого определили условия вызова исключения - вызов метода деления на 0. В следующем тесте выполняется проверка метода divide. При делении на 0 вызывается исключение.
@Test
public void testDevide()
{
when(mcalc.divide(15.0, 3)).thenReturn(5.0);
assertEquals(calc.divide(15.0, 3), 5.0, 0);
// проверка вызова метода
verify(mcalc).divide(15.0, 3);
// создаем исключение
RuntimeException exception = new RuntimeException ("Division by zero");
// определяем поведение
doThrow(exception).when(mcalc).divide(15.0, 0);
assertEquals(calc.divide(15.0, 0), 0.0, 0);
verify(mcalc).divide(15.0, 0);
}
4. Использование интерфейса org.mockito.stubbing.Answer<T>Иногда описание поведения mock объекта требует определенной проверки с усложнением логики. В этом случае можно использовать интерфейс Answer<T>, который позволяет реализовать заглушки методов со сложным поведением. В следующем тесте testThenAnswer при вызове метода сложения с определенными параметрами mcalc.add(11.0, 12.0) будет вызван метод answer, который подготовит ответ. Параметр InvocationOnMock позволяет получить информацию о вызываемом методе и параметрах.
// метод обработки ответа
private Answer<Double> answer = new Answer<Double>() {
@Override
public Double answer(InvocationOnMock invocation) throws Throwable
{
// получение объекта mock
Object mock = invocation.getMock();
System.out.println ("mock object : " + mock.toString());
// аргументы метода, переданные mock
Object[] args = invocation.getArguments();
double d1 = (double) args[0];
double d2 = (double) args[1];
double d3 = d1 + d2;
System.out.println ("" + d1 + " + " + d2);
return d3;
}
};
@Test
public void testThenAnswer()
{
// определение поведения mock для метода с параметрами
when(mcalc.add(11.0, 12.0)).thenAnswer(answer);
assertEquals(calc.add(11.0,12.0), 23.0, 0);
}
5. Использование шпиона spy на реальных объектахMockito позволяет подключать к реальным объектам «шпиона» spy, контролировать возвращаемые методами значения и отслеживать количество вызовов методов. В следующем тесте создадим шпиона scalc, который подключим к реальному калькулятору и будем вызывать метод double15(). Необходимо отметить, что метод реального объекта double15 должен вернуть значение 15. Однако Mockito позволяет переопределить значение и согласно вновь назначенному условию новое значение должно быть 23.
@Test
public void testSpy()
{
Calculator scalc = spy(new Calculator());
when(scalc.double15()).thenReturn(23.0);
// вызов метода реального класса
scalc.double15();
// проверка вызова метода
verify(scalc).double15();
// проверка возвращаемого методом значения
assertEquals(23.0, scalc.double15(), 0);
// проверка вызова метода не менее 2-х раз
verify(scalc, atLeast(2)).double15();
}
В результате выполнения теста видим, что метод возвращает значение 23. Таким образом, фреймворк Mockito в сочетании с JUnit можно использовать для тестов реального класса. При этом, можно проверить, вызывался ли метод, сколько раз и с какими параметрами. Кроме этого, можно создавать заглушки только для некоторых методов. Это позволяет проверить поведение одних методов, используя заглушки для других. 6. Проверка вызова метода с задержкой, timeoutФреймворк Mockito позволяет выполнить проверку вызова определенного метода в течение заданного в timeout времени. Задержка времени определяется в милисекундах.
@Test
public void testTimout()
{
// определение результирующего значения mock для метода
when(mcalc.add(11.0, 12.0)).thenReturn(23.0);
// проверка значения
assertEquals(calc.add(11.0,12.0), 23.0, 0);
// проверка вызова метода в течение 10 мс
verify(mcalc, timeout(100)).add(11.0, 12.0);
}
7. Использование в тестах java классовВ следующем тесте при создании mock объектов используются java классы Iterator и Comparable. После этого определяются условия проверок и выполняются тесты.
@Test
public void testJavaClasses()
{
// создание объекта mock
Iterator<String> mis = mock(Iterator.class);
// формирование ответов
when(mis.next()).thenReturn("Привет").thenReturn("Mockito");
// формируем строку из ответов
String result = mis.next() + ", " + mis.next();
// проверяем
assertEquals("Привет, Mockito", result);
Comparable<String> mcs = mock(Comparable.class);
when(mcs.compareTo("Mockito")).thenReturn(1);
assertEquals(1, mcs.compareTo("Mockito"));
Comparable<Integer> mci = mock(Comparable.class);
when(mci.compareTo(anyInt())).thenReturn(1);
assertEquals(1, mci.compareTo(5));
}
Скачать исходный кодИсходный код рассмотренного примера использования фреймворка Mockito с JUnit при модульном тестировании приложений в виде проекта Eclipse можно скачать здесь (12.4 Кб). |
