410013796724260
• Webmoney
R335386147728
Z369087728698
Тестирование программы, JUnitJUnit — библиотека для модульного тестирования программ Java. Созданный Кентом Беком и Эриком Гаммой, JUnit принадлежит семье фреймворков xUnit для разных языков программирования, берущей начало в SUnit Кента Бека для Smalltalk. JUnit породил экосистему расширений — JMock, EasyMock, DbUnit, HttpUnit и т. д. Библиотека 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); } } Необходимость использования JUnitJUnit позволяет в любой момент быстро убедиться в работоспособности кода. Если программа не является совсем простой и включает множество классов и методов, то для её проверки может потребоваться значительное время. Естественно, что данный процесс лучше автоматизировать. Использование 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); } } //... } Дополнительные возможности, TestSuiteJUnit 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 ?
Примеры использования аннотаций с параметрами, 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 RuleJUnit позволяет использовать определенные разработчиком правила до и после выполнения теста, которые расширяют функционал. Например, есть встроенные правила для задания таймаута для теста (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; } } } } Список основных аннотаций
Список типов проверок Asserts
Пример 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. Но как видно из приведенных примеров, фреймворк достаточно прост в использовании, дополнительных возможностей немного, но есть возможность расширения с помощью правил и запускалок. |