Материал: Автоматизация тестирования программных систем средствами Java технологий

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

2. JUnit (Модульное тестирование)

.1 Описание

Оболочки модульного тестирования - это программные средства для разработки тестов, включающие: построение, выполнение тестов и создание отчетов.

Первая оболочка модульного тестирования SUnit была создана Кентом Беком в 1999 году для языка Smalltalk. Позднее Эрик Гамма создал JUnit. Сейчас существует множество оболочек для модульного тестирования, которые известны как программные средства семейства XUnit. JUnit - реализация XUnit, наиболее широко используемая и расширенная версия оболочек модульного тестирования. JUnit создан на языке Java и используется для тестирования Java кода.

Модульное тестирование или unit testing - процесс проверки на корректность функционирования отдельных частей исходного кода программы путем запуска тестов в искусственной среде. Под частью кода в Java следует понимать исполняемый компонент. С помощью модульного тестирования обычно тестируют низкоуровневые элементы кода - такие, как методы. JUnit позволяет вне исследуемого класса создавать тесты, при выполнении которых произойдет корректное завершение программы. Кроме основного положительного сценария может выполняться проверка работоспособности системы в альтернативных сценариях, например, при генерации методом исключения как реакция на ошибочные исходные данные.

Оценивая каждую часть кода изолированно и подтверждая корректность ее работы, точно установить проблему значительно проще, чем если бы элемент был частью системы.

Технология позволяет и предлагает сделать более тесной связь между разработкой кода и его тестированием, а также предоставляет возможность проверить корректность работы класса, не прибегая к пробному выводу при отладке кода.4 в отличие от JUnit 3 полностью построен на аннотациях.

Для использования технологии необходимо загрузить библиотеку JUnit с сервера junit.org и включить архив junit.jar в список библиотек приложения.

При включении модульного тестирования в проект:

тесты разрабатываются для нетривиальных методов системы;

ошибки выявляются в процессе проектирования метода или класса;

в первую очередь разрабатываются тесты на основной положительный сценарий;

разработчику приходится больше уделять внимания альтернативным сценариям поведения, так как они являются источником ошибок, выявляемых на поздних стадиях разработки;

разработчику приходится создавать более сфокусированные на своих обязанностях методы и классы, так как сложный код тестировать значительно труднее;

снижается число новых ошибок при добавлении новой функциональности;

устаревшие тесты можно игнорировать;

тест отражает элементы технического задания, то есть некорректное завершение теста сообщает о нарушении технических требований заказчика;

каждому техническому требованию соответствует тест;

получение работоспособного кода с наименьшими затратами.

.2 Аннотация @Test

Аннотация помечает метод как тестовый, что позволяет использовать возможности класса org.junit.Assert и запускать его в режиме тестирования. Метод, предназначенный для функционирования в качестве теста, достаточно промаркировать аннотацией @Test.

Тестовый метод должен всегда объявляться как public void. Аннотация может использовать параметры:- определяет ожидаемый класс исключения;- определяет время, превышение которого делает тест ошибочным.

.3 Наиболее часто используемые методы

Метод assertEquals() проверяет на равенство значений expected и actual с возможной погрешностью delta. При выполнении заданных условий сообщает об успешном завершении, в противном случае - об аварийном завершении теста. При аварийном завершении генерируется ошибка java.lang.AssertionError.

Все методы класса Assert в качестве возвращаемого значения имеют тип void.

Среди них можно выделить:

assertTrue(boolean condition)/assertFalse(boolean condition) - проверяет на истину/ложь значение condition;

assertSame(Object expected, Object actual) - проверяет, ссылаются ли ссылки на один и тот же объект;

assertNotSame(Object unexpected, Object actual) - проверяет, ссылаются ли ссылки на различные объекты;

assertNull(Object object)/assertNotNull(Object object) - проверяет, имеет или не имеет ссылка значение null;

assertThat(T actual, Matcher<T> matcher) - проверяет выполнение условия;() - вызывает ошибку, используется для проверки, достигнута ли определенная часть кода или для заглушки, сообщающей, что тестовый метод пока не реализован.

Все перечисленные методы имеют перегруженную версию с параметром типа String в первой позиции, в который можно передавать сообщение, выводимое при аварийном завершении теста.

В дополнение к классу Assert с его методами жесткой проверки прохождения теста разработан класс org.junit.Assume. Методы этого класса в случае невыполнения предполагаемого условия при работе теста сообщают только о том, что предположение не исполнилось, не генерируя при этом никаких ошибок. Методы класса предполагают, что:(Throwable t) - тестируемый метод завершится, не вызвав исключения;(Object…objects) - передаваемый аргумент(ы) не является ссылкой на null;

assumeThat(T actual, Matcher<T> matcher) - условие выполнится;

assumeTrue(boolean b) - значение передаваемого аргумента истинно.

.4 Фикстуры

Фикстура (Fixture) - состояние среды тестирования, которое требуется для успешного выполнения тестового метода. Может быть представлено набором каких-либо объектов, состоянием базы данных, наличием определенных файлов, соединений и проч.

В версии JUnit 4 аннотации позволяют исполнять одну и ту же фикстуру для каждого теста или всего один раз для всего класса, или не исполнять ее совсем.

Предусмотрено четыре аннотации фикстур - две для фикстур уровня класса и две для фикстур уровня метода.

@BeforeClass - запускается только один раз при запуске теста.

@Before - запускается перед каждым тестовым методом (используется, если необходимо выполнить какое-либо повторяющееся действие).

@After - запускается после каждого метода.

@AfterClass - запускается после того, как отработали все тестовые методы.

Использование фикстур позволяет выделить этапы и точно определять моменты, например, создания/удаления объекта, инициализации необходимых ресурсов, очистки памяти и прочего.

.5 Тестирование исключительных ситуаций

При тестировании альтернативных сценариев работы метода часто требуется точно определить тип генерируемого методом исключения на основе переданных некорректных параметров. Если тест выдает исключение, то инфраструктура тестирования сообщает о корректном результате его исполнения.

Аннотацию @Test при необходимости тестирования генерации конкретного исключения (например, деление на 0) следует использовать с параметром expected. Параметр предназначен для задания типа исключения, которое данный тест должен генерировать в процессе своего выполнения.

.6 Ограничение по времени


2.7 Игнорирование тестов

При контроле корректности функционирования бизнес-логики приложений до появления JUnit 4 игнорирование неудачных, незавершенных или устаревших тестов представляло определенную проблему. Аннотация @Ignore заставляет инфраструктуру тестирования проигнорировать данный тестовый метод.

Аннотация предусматривает наличие комментария о причине игнорирования теста, полезного при следующем к нему обращении.

3. Примеры

Рассмотрим первый пример: в программе имеется 2 метода, которые получают на вход целые числа. В первом случае результатом является умножение чисел, во втором - деление.

Код программы:

package Clc;

public class Main {

public void main(String[] args){

this.mult(2, 5);

this.del(6, 3);

}

public int mult(int a, int b){

return a*b;

}

public double del(int a, int b){

return a/b;

}

}

Теперь рассмотрим код тестового класса:

package Clc;

import static org.junit.Assert.*;

import org.junit.Test;

public class MainTest {

@Test

public void testMult() {c = new Main();

int prod = c.mult(3, 5);("Умножение выполнено неверно", prod, 16);

}

@Test

public void testDel() {c = new Main();("Деление неверно",c.del(20, 2),11);

}

@Test

public void testDelEx() {c = new Main();

try{.del(2, 0);("Деление на 0");

}

catch(Exception e){.out.println("Попытка деления на 0");

}

}

@Test (expected = Exception.class)

public void testDelNol(){

new Main().del(3, 0);

}

}

Во-первых мы импортируем две библиотеки, которые нам понадобятся для выполнения тестов: org.junit.Assert.* и org.junit.Test. Это основные библиотеки для создания тестов в JUnit. В нашем случае работа ведётся на JUnit 4.11.

Методом assertEquals() тестового метода testMult() мы сравниваем действительное значение (то, которое получили в ходе выполнения метода mult() класса Main.java) с тем значением, которое нужно получить на выходе (в нашем случае значение заведомо ложное).

Методом testDel() делаем то же самое только для деления (тестируем метод del() класса Main.java).

Третий метод - testDelNol() как раз показывает тестирование исключений, когда программа каким-то образом может пропустить деление на ноль. После аннотации теста можно увидеть дополнительный параметр (expected = Exception.class. Он говорит о том, что мы заведомо ожидаем ошибку выполнения теста.

Тестировать исключения можно и другим способом, как показано в методе testDelEx(). Здесь уже кому как удобно.

Как можно заметить из кода тестового класса, каждая отдельная операция тестируется в отдельном методе. Это делается потому, что если у Вас в одном методе будет тестироваться несколько операций, то при первом невыполнении какого-либо условия дальнейшие условия тестового метода проверяться не будут. Это не страшно, программа небольшая программа. Но подумайте, что будет, если у Вас 100 или более тестов. Трудозатраты на выявление ошибки будут очень велики.

Посмотрим на выполнение этого тестового класса:


Последние 2 теста выполнены успешно, так как на выходе мы получили ожидаемую ошибку.

Рассмотри второй пример. Здесь будет простое матричное умножение.

Вспомогательный класс заполнения матриц:

package matrix_m;

public class MMult {

int m,n;

public MMult(int columns, int rows){= columns;= rows;= new double [m][n];

}

double [][] matrix;

int rows(){

return n;

}

int column(){

return m;

}

double element (int column, int row){

return matrix[column][row];

}

}

double[] row(int index){

return matrix[index];

}

double[] column(int index){

double[] tmp = new double[this.n];

for (int i = 0; i < index; i++)[i][index] = tmp[i];

return tmp;

}

}

Основной класс:

package matrix_m;

import java.util.Scanner;

public class Main {

public Scanner in = new Scanner(System.in);

public int row1, col2, col, row;

public static void main(String[] args){mmultiply = new Main();first = mmultiply.read();second = mmultiply.read();prod = mmultiply.prod(first, second);

if (prod == null).out.println("Эти матрицы не могут быть перемножены");

else.print(prod);

}read(){

System.out.println("Введите размерность матрицы");

col = in.nextInt();= in.nextInt();mx = new MMult(col,row);

if(mx.m != 0 || mx.n != 0){.out.println("Введите элементы этого массива");

for (int i = 0; i < mx.column(); i++){

for (int j = 0; j < mx.rows(); j++){

double tmp = in.nextDouble();.update(i, j, tmp);

}

}

}

else{.out.println("Введена нулевая размерность");

}

return mx;

}

public void print(MMult matrix){

for (int i = 0; i < matrix.column(); ++i, System.out.println())

for (int j = 0; j < matrix.rows(); ++j).out.printf(" %10.4f", matrix.element(i, j));

}prod(MMult first, MMult second){

int col1 = first.column(), row2 = second.rows();= first.rows();= second.column();res = new MMult(first.column(),second.rows());

if(row1 == col2){

for (int i = 0; i < col1; i++)

for(int k = 0; k < row2; k++)

for (int j = 0; j < col2; j++).update(i, k, res.element(i, k)+ first.element(i, j) * second.element(i, k));

return res;

}

else

return null;

}

}

Тестовый класс:

import static org.junit.Assert.*;

import org.junit.Test;

public class MainTest {pr = new Main();first = pr.read();second = pr.read();

@Test

public void testMain() {("Матрицы не перемножены",pr.prod(first, second)== null);

}

@Test

public void testRead() {("Введено не числовое значение", pr.in.hasNextInt() == false);

if(pr.col == 0 || pr.row == 0){("Введена нулевая размерность");

}

}

@Test

public void testProd() {

assertTrue("Колличество строк первой и столбцов второй матриц не совпадают", pr.row1 != pr.col2);

}

}

Здесь при тестировании мы проверяем были ли умножены матрицы, (testMain()), на введении нулевой размерности матрицы или не числового значения(testRead()), и на то, могут ли быть матрицы перемножены(testProd()).

Выполнение теста(для просмотра результата все значения были введены ложные):




В результате проделанной работы были изучены основы тестирования программного обеспечения. Используя полученные знания, был написан программный комплекс. Особое внимание было уделено оболочке модульного тестирования JUnit для тестирования программного обеспечения при разработке. В дальнейшем планируется изучить тестирование Web-приложений.

Список использованных источников

1.      Степанченко И.В. Методы тестирования программного обеспечения: Учеб. пособие / Степанченко И.В. - ВолгГТУ, Волгоград,2006. - 76 с.

.        Синицын, С. В. Верификация программного обеспечения. / С.В. Синицин, Н.Ю. Налютин. - М.:2006. - 158 с.

.        Канер, С. Тестирование программного обеспечения. Фундаментальные концепции менеджмента бизнес-приложений / С. Канер, Дж. Фолк, Е. Кек

.        Тамре, Л. Введение в тестирование программного обеспечения / Л. Тамре; Пер. с англ. М.: Издательский дом "Вильямс", 2003. 368 с.

.        Петренко А., Бритвина Е., Грошев С., Монахов А. Тестирование на основе моделей // «Открытые системы», 2003.

.        Дастин Э., Рэшка Д., Пол Д. Автоматизированное тестирование программного обеспечения. Внедрение, управление и эксплуатация. -М.: Изд-во Лори, 2003. 567 с.

.        Макгрегор Д., Сайке Д. Тестирование объектно-ориентированного программного обеспечения. Практическое пособие. К.: ООО «ТИД «ДС», 2002. - 432с.