Basic Java Tools for Building & Testing Apps
Скачать 4.76 Mb.
|
Список типов проверок Asserts Тип проверки Описание fail() fail(String message) прерывание теста с ошибкой, т.е. тест будет неудачным assertTrue(boolean condition) assertTrue(java.lang.String message, boolean condition) проверка на равенство условия condition значению true assertFalse(boolean condition) assertFalse(String message, boolean condition) проверка на равенство условия condition значению false assertEquals(<тип> expected, <тип> actual) assertEquals(String message, <тип> expected, <тип> actual) проверка на равенство; <тип> — это Object, int, double и т.д. assertArrayEquals(byte[] expecteds, byte[] actuals) assertArrayEquals(String message, <тип>[] expecteds, <тип>[] actuals) проверка массивов на равенство; аналогично assertEquals; <тип> — это Object, int, double и т.д. assertNotNull(Object object) assertNotNull(String message, Object object) проверка, что Object не null assertNull(Object object) assertNull(String message, Object object) проверка, что Object null assertSame(Object expected, Object actual) assertSame(String message, Object expected, Object actual) проверка на равенство двух объектов expected и actual, т.е. один и тот же объект Пример JUnit тестирования Для демонстрации основных возможностей JUnit, используем примитивный java класс FuncMath, который имеет два метода - нахождение факториала неотрицательного числа и суммы двух чисел. Кроме того, в экземпляре класса будет находится счетчик вызовов методов. public class FuncMath { int calls ; public int getCalls () { return calls ; } public long factorial ( int number ) { calls ++; if ( number < 0 ) throw new IllegalArgumentException (); long result = 1 ; if ( number > 1 ) { for ( int i = 1 ; i < = number ; i ++) result = result * i ; } return result ; } public long plus ( int num1 , int num2 ) { calls ++; return num1 + num2 ; } } Иногда для выполнения каждого тестового сценария необходим некоторый контекст, например, заранее созданные экземпляры классов. А после выполнения нужно освободить зарезервированные ресурсы. В этом случае используют аннтоации @Before и @After. Метод, помеченный @Before будет выполняться перед каждым тестовым случаем, а метод, помеченный @After - после каждого тестового случая. Если же инициализацию и освобождение ресурсов нужно сделать всего один раз - соответственно до и после всех тестов - то используют пару аннотаций @BeforeClass и @AfterClass. Тестовый класс с несколькими сценариями будет иметь следующий вид : import org junit Test ; import org junit After ; import org junit Before ; import org junit Assert ; import org junit AfterClass ; import org junit BeforeClass ; public class JUnit_funcMath extends Assert { private FuncMath math ; @Before public void init () { math = new FuncMath (); } @After public void tearDown () { math = null ; } @Test public void calls () { assertEquals ( "math.getCalls() != 0" , 0 , dao getConnection ()); math factorial ( 1 ); assertEquals ( 1 , math getCalls ()); math factorial ( 1 ); assertEquals ( 2 , math getCalls ()); } @Test public void factorial () { assertTrue ( math factorial ( 0 ) == 1 ); assertTrue ( math factorial ( 1 ) == 1 ); assertTrue ( math factorial ( 5 ) == 120 ); } @Test ( expected = IllegalArgumentException class ) public void factorialNegative () { math factorial (- 1 ); } @Ignore @Test public void todo () { assertTrue ( math plus ( 1 , 1 ) == 3 ); } } Метод calls тестирует правильность счетчика вызовов. Метод factorial проверяет правильность вычисления факториала для некоторых стандартных значений. Метод factorialNegative проверяет, что для отрицательных значений факотриала будет брошен IllegalArgumentException. Метод todo будет проигнорирован. В заключении следует отметить, что в статье представлены не все возможности использования JUnit. Но как видно из приведенных примеров, фреймворк достаточно прост в использовании, дополнительных возможностей немного, но есть возможность расширения с помощью правил и запускалок JUnit и фреймворк Mockito Разработчикам программного обеспечения на разных этапах своей деятельности приходится сталкиваться с тремя стратегиями тестирования : функциональным, интеграционным и модульным тестированием. Все они используются для тестирования приложений разными способами, и каждая из стратегий имеет определенную цель. К сожалению, ни одна из стратегий не даёт стопроцентной гарантии обнаружения всех имеющихся ошибок. И даже комбинация всех трёх стратегий не может дать такой гарантии. Но их сочетание позволяет существенно снизить количество ошибок и убедить разработчика, что приложение функционирует согласно предъявленным требованиям. Функциональное тестирование Проведение функционального тестирования, как правило, связано с созданием специальной группы специалистов, занимающихся тестированием. На этом этапе приложение развертывается в действующем окружении и проверяется его соответствие ТЗ (Техническому Заданию) и предъявленным функциональным требованиям. Команда тестеровщиков использует комплекс автоматизированных и ручных тестов. Автоматизировать процесс функционального тестирования можно, если приложение включает API (Application Programming Interface) - интерфейс прикладного программирования, на котором оно построено. Однако наличие интерфейса в приложении (desktop, web) существенно снижает возможности полной автоматизации данного процесса. Интеграционное тестирование Стратегия интеграционного тестирования основывается на проверке прикладного кода в окружении, близком к фактическому окружению, но не являющимся им. Главная цель данной стратегии – убедиться в правильности взаимодействия кода с внешними ресурсами и взаимодействия различных технологий в приложении между собой. В интеграционном тестировании не требуется использовать фиктивные данные, как при модульном тестировании. Вместо этого в интеграционных тестах часто используются базы данных, находящиеся в памяти, которые легко можно создавать и уничтожать во время выполнения тестов. База данных в памяти – это самая настоящая база данных, что дает возможность проверить правильность работы сущностей JPA. Но все же эта база данных не совсем настоящая – она лишь имитирует настоящую базу данных для целей интеграционного тестирования. Модульное тестирование Целью модульного тестирования является проверка работы прикладной логики всего приложения или отдельных его частей при разных исходных данных, и анализ правильности получаемых результатов. Несмотря на то, что цель модульного тестирования выглядит простой и понятной, реализация этого типа тестирования может оказаться очень сложным и запутанным делом, особенно при наличии «старого» кода. Основные приемы проведения модульного тестирования опираются на следующие базовые принципы : внешние ресурсы не используются, т.е. недопустимо подключение к базам данных, веб-службам и т.п.; каждый класс имеет свой тест; тестируются только общедоступные методы или интерфейсы, а внутренний код тестируется за счет изменения входных данных; для получения данных, требуемых тестируемой логике, должны создаваться фиктивные зависимости. При проведении модульного тестирования для создания фиктивных классов-зависимостей можно использовать простой, но мощный фреймворк Mockito совместно с JUnit Фреймворк Mockito Фреймворк Mockito предоставляет ряд возможностей для создания заглушек вместо реальных классов или интерфейсов при написании JUnit тестов. Mockito можно скачать с сайта https://code.google.com/p/mockito , либо определить в зависимостях (dependencies) в maven проекте : org.mockito mockito-core 1.9.5 test Наибольшее распространение получили следующие возможности Mockito : создание заглушек для классов и интерфейсов; проверка вызыва метода и значений передаваемых методу параметров; использование концепции «частичной заглушки», при которой заглушка создается на класс с определением поведения, требуемое для некоторых методов класса; подключение к реальному классу «шпиона» spy для контроля вызова методов. Синтаксис создания заглушки 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 предоставляет следующие методы : atLeast (int min) - не меньше min вызовов; atLeastOnce () - хотя бы один вызов; atMost (int max) - не более max вызовов; times (int cnt) - cnt вызовов; never () - вызовов не было; Следующий тест демонстрирует контроль количества вызовов метода 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 Иногда описание поведения mock объекта требует определенной проверки с усложнением логики. В этом случае можно использовать интерфейс 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 позволяет подключать к реальным объектам «шпиона» |