Basic Java Tools for Building & Testing Apps
Скачать 4.76 Mb.
|
JUnit была портирована на другие языки, включая PHP (PHPUnit), C# (NUnit), Python (PyUnit), Fortran (fUnit), Delphi (DUnit), Free Pascal (FPCUnit), Perl (Test::Unit), C++ (CPPUnit), Flex (FlexUnit), JavaScript (JSUnit). JUnit – это Java фреймворк для тестирования, т. е. тестирования отдельных участков кода, например, методов или классов. Опыт, полученный при работе с JUnit, важен в разработке концепций тестирования программного обеспечения. Пример теста JUnit import org junit Test ; import junit framework Assert ; public class MathTest { @Test public void testEquals () { Assert assertEquals ( 4 , 2 + 2 ); Assert assertTrue ( 4 == 2 + 2 ); } @Test public void testNotEquals () { Assert assertFalse ( 5 == 2 + 2 ); } } JUnit'>Необходимость использования JUnit JUnit позволяет в любой момент быстро убедиться в работоспособности кода. Если программа не является совсем простой и включает множество классов и методов, то для её проверки может потребоваться значительное время. Естественно, что данный процесс лучше автоматизировать. Использование JUnit позволяет проверить код программы без значительных усилий и не занимает много времени. Юнит тесты классов и функций являются своего рода документацией к тому, что ожидается в результате их выполнения. И не просто документацией, а документацией которая может автоматически проверять код на соответствие предъявленным функциям. Это удобно, и часто тесты разрабатывают как вместе, так и до реализации классов. Разработка через тестирование — крайне популярная технология создания серьезного программного обеспечения. Виды тестирования и место JUnit тестирования в классификации Тестирование программного обеспечение можно разделить на два вида: тестирование черного ящика; тестирование белого ящика. Во время тестирования программы как черного ящика внутренняя структура приложения в расчет не принимается. Все, что имеет значение, это функциональность, которую приложение должно обеспечить. При тестировании программы как белого ящика во внимание принимается внутренняя структура, т.е. класс и методы. Кроме этого, тестирование можно разделить на четыре уровня: юнит тесты — тестирование отдельных участков кода; интеграционное тестирование — тестирование взаимодействия и совместной работы компонентов; системное тестирование — тестирование всей системы как целого; приемное тестирование — итоговое тестирование готовой системы на соответствие требованиям. Юнит тестирование по определению является тестированием белого ящика. Используется unit тестирование в двух вариантах - JUnit 3 и JUnit 4. Рассмотрим обе версии, так как в старых проектах до сих пор используется 3-я версия, которая поддерживает Java 1.4. JUnit 3 Для создания теста следует наследовать тест-класс TestCase, переопределить методы setUp и tearDown при необходимости, ну и самое главное — разработать тестовые методы, наименование которых должно начинаться с аббривиатуры "test". При запуске теста сначала создается экземляр тест-класса (для каждого теста в классе отдельный экземпляр класса), затем выполняется метод setUp, запускается сам тест, ну и в завершение выполняется метод tearDown. Если какой-либо из методов вызывает исключение, тест считается провалившимся. Примечание : тестовые методы должны быть public void, могут быть static. Тесты состоят из выполнения некоторого кода и проверок. Проверки чаще всего выполняются с помощью класса Assert хотя иногда используют ключевое слово assert. В качестве примера рассмотрим утилиту для работы со строками, включающую методы для проверки пустой строки и представления последовательности байт в виде 16-ричной строки: public class JUnit3StringUtilsTest extends TestCase { private final Map toHexStringData = new HashMap (); protected void setUp () throws Exception { toHexStringData put ( "" , new byte [ 0 ]); toHexStringData put ( "01020d112d7f" , new byte []{ 1 , 2 , 13 , 17 , 45 , 127 }); toHexStringData put ( "00fff21180" , new byte []{ 0 ,- 1 ,- 14 , 17 ,- 128 }); //... } protected void tearDown () throws Exception { toHexStringData clear (); } public void testToHexString () { for ( Iterator iterator = toHexStringData keySet (). iterator (); iterator hasNext ();) { final String expected = ( String ) iterator next (); final byte [] testData = ( byte []) toHexStringData get ( expected ); final String actual = StringUtils toHexString ( testData ); assertEquals ( expected , actual ); } } //... } Дополнительные возможности, TestSuite JUnit 3 имеет несколько дополнительных возможностей. Например, можно группировать тесты. Для этого необходимо использовать класс TestSuite: public class JUnit3StringUtilsTestSuite extends TestSuite { public JUnit3StringUtilsTestSuite () { addTestSuite ( StringUtilsJUnit3Test class ); addTestSuite ( OtherTest1 class ); addTestSuite ( OtherTest2 class ); } } Можно исполнение теста повторить несколько раз. Для этого используется RepeatedTest : public class JUnit3StringUtilsRepeatedTest extends RepeatedTest { public JUnit3StringUtilsRepeatedTest () { super ( new JUnit3StringUtilsTest (), 100 ); } } Наследуя тест-класс от ExceptionTestCase, можно проверить код на выброс исключения : public class JUnit3StringUtilsExceptionTest extends ExceptionTestCase { public JUnit3StringUtilsExceptionTest ( final String name ) { super ( name , NullPointerException class ); } public void testToHexString () { StringUtils toHexString ( null ); } } Как видно из примеров все довольно просто и ничего лишнего - минимум кода для JUnit тестирования. JUnit 4 В JUnit 4 добавлена поддержка новых возможностей из Java 5.0; тесты могут быть объявлены с помощью аннотаций. При этом существует обратная совместимость с предыдущей версией фреймворка. Практически все рассмотренные выше примеры будут работать и в JUnit 4 за исключением RepeatedTest, который отсутствует в новой версии. Какие внесены изменения появились в JUnit 4? Рассмотрим тот же пример, но уже с использованием новых возможностей : public class JUnit4StringUtilsTest extends Assert { private final Map < String , byte []> toHexStringData = new HashMap < String , byte []>(); @Before public static void setUpToHexStringData () { toHexStringData put ( "" , new byte [ 0 ]); toHexStringData put ( "01020d112d7f" , new byte []{ 1 , 2 , 13 , 17 , 45 , 127 }); toHexStringData put ( "00fff21180" , new byte []{ 0 ,- 1 ,- 14 , 17 ,- 128 }); //... } @After public static void tearDownToHexStringData () { toHexStringData clear (); } @Test public void testToHexString () { for ( Map Entry < String , byte []> entry : toHexStringData entrySet ()) { final byte [] testData = entry getValue (); final String expected = entry getKey (); final String actual = StringUtils toHexString ( testData ); assertEquals ( expected , actual ); } } } Что изменилось в JUnit 4 ? Для упрощения работы можно наследоваться от класса Assert, хотя это необязательно. Аннотация @Before обозначает методы, которые будут вызваны перед исполнением тестов. Методы должны быть public void. Здесь обычно размещаются предустановки для теста, в нашем случае это генерация тестовых данных (метод setUpToHexStringData). Можно использовать аннотацию @BeforeClass, которая обозначает методы, которые будут вызваны до создания экземпляра тест-класса; методы должны быть public static void. Данную аннотацию (метод) имеет смысл использовать для тестирования в случае, когда класс содержит несколько тестов, использующих различные предустановки, либо когда несколько тестов используют одни и те же данные, чтобы не тратить время на их создание для каждого теста. Аннотация @After обозначает методы, которые будут вызваны после выполнения тестов. Методы должны быть public void. Здесь размещаются операции освобождения ресурсов после теста; в нашем случае — очистка тестовых данных (метод tearDownToHexStringData). Аннотация @AfterClass связана по смыслу с @BeforeClass, но выполняет методы после тестирования класса. Как и в случае с @BeforeClass, методы должны быть public static void. Аннотация @Test обозначает тестовые методы. Как и ранее, эти методы должны быть public void. Здесь размещаются сами проверки. Кроме того, в данной аннотации можно использовать два параметра, expected — задает ожидаемое исключение и timeout — задает время, по истечению которого тест считается провалившимся. Примеры использования аннотаций с параметрами, JUnit Test : @Test ( expected = NullPointerException class ) public void testToHexStringWrong () { StringUtils toHexString ( null ); } @Test ( timeout = 1000 ) public void infinity () { while ( true ); } Игнорирование выполнения теста, JUnit Ignore Если один из тестов по какой-либо серьезной причине необходимо отключить, например, тест постоянно завершается с ошибкой. Исправление теста можно отложить до светлого будущего аннотированием @Ignore. Если поместить эту аннотацию на класс, то все тесты в этом классе будут отключены. @Ignore @Test ( timeout = 1000 ) public void infinity () { while ( true ); } Правила тастирования, JUnit Rule JUnit позволяет использовать определенные разработчиком правила до и после выполнения теста, которые расширяют функционал. Например, есть встроенные правила для задания таймаута для теста (Timeout), для задания ожидаемых исключений (ExpectedException), для работы с временными файлами(TemporaryFolder) и др. Для объявления правила необходимо создать public не static поле типа производного от MethodRule и аннотировать его с помощью ключевого слова Rule. public class JUnitOtherTest { @Rule public final TemporaryFolder folder = new TemporaryFolder (); @Rule public final Timeout timeout = new Timeout ( 1000 ); @Rule public final ExpectedException thrown = ExpectedException none (); @Ignore @Test public void anotherInfinity () { while ( true ); } @Test public void testFileWriting () throws IOException { final File log = folder newFile ( "debug.log" ); final FileWriter logWriter = new FileWriter ( log ); logWriter append ( "Hello, " ); logWriter append ( "World!!!" ); logWriter flush (); logWriter close (); } @Test public void testExpectedException () throws IOException { thrown expect ( NullPointerException class ); StringUtils toHexString ( null ); } } Наборы тестов, JUnit Suite, SuiteClasses Запуск теста может быть сконфигурирован с помощью аннотации @RunWith. Тестовые классы, которые содержат в себе тестовые методы, можно объединить в наборы тестов (Suite). Например, создано два класса тестирования объектов : TestFilter, TestConnect. Эти два тестовых класса можно объединить в один тестовый класс TestWidgets.java : package com objects ; import org junit runner RunWith ; import org junit runners Suite ; @RunWith ( Suite class ) @Suite SuiteClasses ({ TestFilter class , TestConnect class }) public class TestWidgets {} Для настройки запускаемых тестов используется аннотация @SuiteClasses, в которую включены тестовые классы. Аннотация Categories Аннотация Categories позволяет объединить тесты в категории (группы). Для этого в тесте определяется категория @Category, после чего настраиваются запускаемые категории тестов в Suite. Это может выглядеть следующим образом: public class JUnitStringUtilsCategoriesTest extends Assert { //... @Category ( Unit class ) @Test public void testIsEmpty () { //... } //... } @RunWith ( Categories class ) @Categories IncludeCategory ( Unit class ) @Suite SuiteClasses ({ JUnitOtherTest class , JUnitStringUtilsCategoriesTest class }) public class JUnitTestSuite {} Аннотация, JUnit Parameterized Аннотация Parameterized позволяет использовать параметризированные тесты. Для этого в тест-классе объявляется статический метод, возвращающий список данных, которые будут использованы в качестве аргументов конструктора класса. @RunWith ( Parameterized class ) public class JUnitStringUtilsParameterizedTest extends Assert { private final CharSequence testData ; private final boolean expected ; public JUnitStringUtilsParameterizedTest ( final CharSequence testData , final boolean expected ) { this testData = testData ; this expected = expected ; } @Test public void testIsEmpty () { final boolean actual = StringUtils isEmpty ( testData ); assertEquals ( expected , actual ); } @Parameterized Parameters public static List < Object []> isEmptyData () { return Arrays asList ( new Object [][] { { null , true }, { "" , true }, { " " , false }, { "some string" , false }, }); } } Параметризирование метода : Theories.class, DataPoints, DataPoint, Theory Аннотация Theories параметризирует тестовый метод, а не конструктор. Данные помечаются с помощью @DataPoints и @DataPoint, тестовый метод — с помощью @Theory. Тест, использующий этот функционал, может выглядеть примерно следующим образом : @RunWith ( Theories class ) public class JUnitStringUtilsTheoryTest extends Assert { @DataPoints public static Object [][] isEmptyData = new Object [][] { { "" , true }, { " " , false }, { "some string" , false }, }; @DataPoint public static Object [] nullData = new Object [] { null , true }; @Theory public void testEmpty ( final Object testData ) { final boolean actual = StringUtils isEmpty (( CharSequence ) testData [ 0 ]); assertEquals ( testData [ 1 ], actual ); } } Порядок выполнения тестов Если необходимо выполнить тест в определенном порядке, то можно воспользоваться аннотацией @FixMethodOrder(MethodSorters.NAME_ASCENDING), определенной в JUnit 4.11. Например : @FixMethodOrder ( MethodSorters NAME_ASCENDING ) public class MyTest { @Test public void test01 (){...} @Test public void test02 (){...} @Test public void test09 (){...} } В противном случае можно использовать следующие 2 подхода. void test01 (); void test02 (); void test09 (); @Test public void testOrder1 () { test1 (); test3 (); } @Test ( expected = Exception class ) public void testOrder2 () { test2 (); test3 (); test1 (); } @Test ( expected = NullPointerException class ) public void testOrder3 () { test3 (); test1 (); test2 (); } @Test public void testAllOrders () { for ( Object [] sample : permute ( 1 , 2 , 3 )) { for ( Object index : sample ) { switch ((( Integer ) index ). intValue ()) { case 1 : test1 (); break ; case 2 : test2 (); break ; case 3 : test3 (); break ; } } } } Список основных аннотаций Аннотация Описание @Test public void testMethod() метод является тестовым @Test(timeout=100) public void testMethod() если время выполнения превысит параметр timeout, то тест будет завершен неудачно @Test (expected = MyException.class) public void testMethod() метод должен выбросить исключение принадлежащие к классу MyException, в противном случае тест будет завершен неудачно @Ignore public void testMethod() игнорировать тестовый метод @BeforeClass public static void testMethod() метод вызывающийся один раз для класса перед выполнением тестовых методов; здесь можно разместить инициализацию которую нужно выполнять только один раз, например, прочитать данные, которые будут использоваться в тестовых методах или создать соединение с базой данных @AfterClass public static void testMethod() метод вызывающийся один раз для класса после выполнения тестовых методов; здесь можно разместить деинициализацию которую нужно выполнять только один раз, например, закрыть соединение с базой данных или удалить данные, которые больше не нужны @Before public static void beforeMethod() метод, вызывающийся перед каждым тестовым методом в тестовом классе; здесь можно выполнить необходимую инициализацию, например, выставить начальные параметры @After public static void afterMethod() метод, вызывающийся после каждого тестового метода в тестовом классе; здесь можно выполнить необходимую деинициализацию, например, удалить данные, которые больше не нужны |