Java. Тестирование JUnit. Пример

Тестирование JUnit. Примеры


Содержание


Поиск на других ресурсах:

1. Понятие о модульном тестировании в Java

В системах программирования на языке Java, в том числе Java Eclipse, введены средства тестирования, которые предусматривают создание и использование автоматических тестов для разработанных программ. Правильно построенное тестирование позволяет свести количество ошибок в программе к нулю.

При разработке современных программных систем популярна методология TDD (Test Driven Development). Основная идея применения TDD заключается в том, что сначала разрабатываются тесты на желаемое поведение программы (системы), а затем пишется рабочий программный код, который с помощью средств рефакторинга проверяется уже созданными тестами. Как следствие, заранее подготовленные тесты позволяют избежать сдачи в эксплуатацию программы, содержащей ошибки.

Для проведения тестирования в Java Eclipse применяется так называемое JUnit-тестирование, которое является фактическим стандартом для тестирования Java-приложений. JUnit — это обычная среда (framework), позволяющая писать тесты в виде классов Java. Эти классы могут запускаться как единое целое с помощью средства выполнения тестов.

JUnit имеет много расширений, которые внедрены во всех современных средах разработки на языке Java. На сегодняшний день JUnit используется в таких направлениях как:

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

JUnit-тестирование используется многими группами тестировщиков (quality assurance) на специально-разработанных веб-сайтах, обеспечивающих автоматизацию применения сквозных функциональных тестов.

В Java Eclipse использование механизма JUnit не требует работающего сервера приложений или действующей базы данных.

 

2. Понятие тестового примера (Test Case). Класс, содержащий тестовые примеры (методы). Запуск теста

При использовании механизма JUnit, рассматриваются две основные единицы (классы):

  • класс, методы которого нужно протестировать;
  • класс, который представляет собой JUnit тест. Этот класс построен по специальным правилам.

Значит, JUnit-тест представляет собой специально разработанный класс, содержащий методы, которые тестируют программный код других классов. В системе Java Eclipse этот класс создается с помощью команды JUnit Test Case (смотрите примеры далее).

Класс содержит ряд методов. Каждый из методов тестирующего класса рассматривается как отдельный тестовый примерTest Case.

Тестовые примеры (методы) JUnit-класса могут быть объявлены с использованием следующих аннотаций (рассматривается версия Java Eclipse 2018-09):

  • @BeforeAll — статический метод (тестовый пример), объявленный с этой инструкцией, вызывается в начале тестирования;
  • @AfterAll — статический метод, объявленный с этой инструкцией, вызывается в конце тестирования. Здесь можно задавать, например, освобождение ресурсов используемых при тестировании;
  • @Test — метод, который объявлен с этой инструкцией, является тестовым примером. Он содержит непосредственно код тестирования. Этот метод использует методы класса org.junit.jupiter.api.Assertions. В этом методе используются методы сравнения, которые начинаются с префикса assert*(). Например, для сравнения двух значений любого примитивного типа используется перегруженный метод assertEquals(). В одном тестовой классе JUnit может быть произвольное количество методов (примеров) тестирования, которые объявлены с аннотацией @Test;
  • @BeforeEach — вызывается перед вызовом каждого тестового примера (метода), который объявлен с аннотацией @Test;
  • @AfterEach — вызывается после завершения каждого тестового примера (метода), который объявлен с аннотацией @Test.

Допускается произвольное количество методов со всеми вышеприведенными аннотациями.

В наиболее общем случае, примерный вид класса, сгенерированный системой Java Eclipse, может быть таким

class TestClass {

  @BeforeAll
  static void setUpBeforeClass() throws Exception {
  }

  @AfterAll
  static void tearDownAfterClass() throws Exception {
  }

  @BeforeEach
  void setUp() throws Exception {
  }

  @AfterEach
  void tearDown() throws Exception {
  }

  @Test
  void testMethod() {
    fail("Not yet implemented");
  }
}

В методе testMethod() нужно вставить собственный код работы методов другого класса (смотрите примеры ниже). После этого, можно запускать тест с помощью специальной команды Java Eclipse.

 

3. Пример, демонстрирующий использование JUnit для проверки правильности решения квадратного уравнения. Пошаговая инструкция
3.1. Условие задачи

Используя технологию JUnit разработать Unit-тест для проверки работы класса SquareEquation, который содержит средства решения квадратного уравнения.

Тест должен размещаться в классе SquareEquationTest и содержать соответствующие методы проверки правильности полученного решения.

Корни квадратного уравнения возвращаются в виде экземпляра класса Roots.

Класс SquareEquationTest содержит соответствующие средства решения квадратного уравнения.

 

3.2. Решение
3.2.1. Создание классов SquareEquation и Roots

Перед созданием классов создается проект стандартным для Java Eclipse способом. В проект добавляются два класса с именами SquareEquation и Roots и формируется код этих классов. Соответственно создается файл SquareEquation.java. После выполненных действий, окно Java Eclipse имеет вид как показано на рисунке 1.

Окно Java Eclipse. Классы Roots и SquareEquation

Рисунок 1. Окно Java Eclipse. Классы Roots и SquareEquation

Классы имеют следующее назначение:

  • Roots — используется для сохранения корней квадратного уравнения (x1, x2) в случае, если уравнение имеет эти корни;
  • SquareEquation — реализует методы решения квадратного уравнения.
  • Класс SquareEquation имеет следующие составляющие:
  • внутренние переменные a, b, c что являются коэффициентами квадратного уравнения;
  • конструктор, инициализирует значения внутренних полей a, b, c;
  • метод Solution(), который возвращает экземпляр класса Roots в случае, если уравнение имеет корни. Если уравнение не имеет корней, то генерируется исключение типа ArithmeticException.

Полный текст классов Roots и SquareEquation выглядит так

// Корни уравнения
class Roots {
  public double x1;
  public double x2;
}

// Класс, решающий квадратное уравнение - этот класс будет протестирован другим классом.
// a*x^2 + b*x + c = 0
public class SquareEquation {

  // Коэффициенты уравнения
  private double a, b, c;

  // Конструктор
  public SquareEquation(double a, double b, double c) {
    this.a = a; this.b = b; this.c = c;
  }

  // Метод, решающий квадратное уравнение
  public Roots Solution() {
    // Дискриминант
    double d = b*b - 4*a*c;

    // Проверка, имеет ли уравнение корни
    if (d<0)
      throw new ArithmeticException("Solution has no roots.");

    // Вычислить результат
    Roots result = new Roots();
    result.x1 = (-b - Math.sqrt(d)) / (2*a);
    result.x2 = (-b + Math.sqrt(d)) / (2*a);

    // Вернуть результат
    return result;
  }
}

После создания классов можно переходить к созданию Unit-теста.

 

3.2.2. Создание класса, необходимого для реализации JUnit тестирования

Для создания Unit-теста в Java Eclipse нужно выбрать последовательность команд

File->New->JUnit Test Case

как показано на рисунке 2.

Java Eclipse. Команда создания Unit-теста в Java

Рисунок 2. Команда создания Unit-теста в Java

В результате откроется окно «New JUnit Test Case» (рисунок 3) в котором нужно указать необходимую информацию о тесте.

Java Eclipse. JUnit-тестирование. Окно создания нового теста

Рисунок 3. Окно создания нового теста. Указание параметров теста

При создании окна система автоматически заполнит некоторые поля. В окне указывается следующая информация.

  1. В поле Source Folder указывается имя папки с исходными файлами проекта. Система автоматически «подтягивает» имя папки с текущим проектом.
  2. В поле Name — задается имя класса, в котором будут размещаться методы тестирования (с аннотациями @BeforeAll, @Test т.д.). В нашем случае предлагается SquareEquationTest (к имени класса SquareEquation добавляется окончание Test). Предложенное имя можно оставить без изменений.
  3. В поле «Which method stubs would you like to create?» с помощью опций указываются методы, которые нужно сформировать в классе. Согласно выбранной опции формируются следующие методы:
    • setUpBeforeClass() — имя метода аннотации @BeforeAll (см п.2);
    • setUp() — имя метода аннотации @BeforeEach;
    • tearDownAfterClass() — имя метода аннотации @AfterAll;
    • tearDown() — имя метода аннотации @AfterEach.

Для нашего теста достаточно активировать только одно поле setUp(). Это позволит добавить метод аннотации @BeforeEach, что будет вызываться перед тестом. В этом методе будет создаваться экземпляр класса SquareEquation (смотрите коды ниже).

  1. В поле «Class under test:» указывается имя класса SquareEquation, методы которого нужно протестировать.
  2. Дополнительно задаются другие опции:
    • выбор модели тестирования «New JUnit Jupiter test»;
    • возможность добавления комментариев;
    • другое.

В нашем случае можно оставить все без изменений.

После выбора кнопки «Next >» откроется следующее окно «New JUnit Test Case» в котором нужно задать перечень методов, которые будут тестироваться. Вид окна показан на рисунке 4. Избранные методы будут объявляться с аннотацией @Test. Это непосредственно тестовые примеры. Все остальные опции в окне можно оставить по умолчанию и перейти далее с помощью кнопки Finish.

Java Eclipse. JUnit-тестирование. Окно выбора методов для тестирования

Рисунок 4. Выбор методов которые будут тестироваться. Выбран метод Solution()

 

3.2.3. Код класса SquareEquationTest

После создания JUnit теста с помощью оконного интерфейса Java Eclipse сгенерирует код класса SquareEquationTest

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SquareEquationTest {

  @BeforeEach
  void setUp() throws Exception {
  }

  @Test
  void testSolution() {
    fail("Not yet implemented");
  }
}

В этом коде сформированы два метода:

  • метод setUp() с аннотацией @BeforeEach. Этот метод будет вызываться первым при тестировании;
  • метод testSolution() с аннотацией @Test. В этом методе будет помещен непосредственно тест функции Solution() класса SquareEquation.

 

3.2.4. Модификация класса SquareEquationTest. Программирование теста

Чтобы протестировать работу класса SquareEquation нужно создать экземпляр этого класса. Поэтому, в текст класса SquareEquationTest дополнительно вводится ссылка на класс SquareEquation

class SquareEquationTest {

  private SquareEquation se;

  ...
}

Затем в методе setUp() создается экземпляр класса SquareEquation, на который указывает ссылка se

@BeforeEach
void setUp() throws Exception {
  // Этот метод вызывается первым.
  // Создать экземпляр класса SquareEquation.
  se = new SquareEquation(2, 1, -3); // 2*x^2 + x - 3 = 0
}

После этого, в методе TestSolution() вписывается код проверки правильности полученного решения для заданных коэффициентов

a = 2
b = 1
c = -3

в экземпляре se

@Test
void testSolution() {
  // Объявить экземпляр класса Roots
  Roots rt = se.Solution();

  // Проверка решения x1
  assertEquals(rt.x1, -1.5);

  // Проверка решения x2
  assertEquals(rt.x2, 1.0);
}

Проверка решения осуществляется с помощью перегруженного метода assertEquals() класса org.junit.jupiter.api.Assertions. Происходит сравнение решения в переменных rt.x1 и rt.x2 с заранее вычисленным (вручную) решением текущего варианта (a = 2, b = 1, c = -3) квадратного уравнения. Если есть совпадение, то решение правильное. О том, что решение правильное, нужна сказать система после запуска теста (следующий пункт).

В целом текст всего модуля тестирования следующий

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SquareEquationTest {

  // Ссылка на класс SquareEquation
  private SquareEquation se;

  @BeforeEach
  void setUp() throws Exception {
    // Этот метод вызывается первым.
    // Создать экземпляр класса SquareEquation
    se = new SquareEquation(2, 1, -3); // 2*x^2 + x - 3 = 0
  }

  @Test
  void testSolution() {
    // Объявить экземпляр класса Roots
    Roots rt = se.Solution();

    // Проверка решения x1
    assertEquals(rt.x1, -1.5);

    // Проверка решения x2
    assertEquals(rt.x2, 1.0);
  }
}

 

3.3. Запуск теста средствами Java Eclipse. Просмотр результата тестирования

После того, как сформирован код класса SquareEquationTest, разработанный тест можно запускать на выполнение. Это осуществляется командами

Run -> Run As -> JUnit Test

или вызовом последовательности комбинаций клавиш Alt + Shift + X, T как показано на рисунке 5.

Java Eclipse. Запуск JUnit-теста

Рисунок 5. Запуск JUnit-теста

Существует другой способ с помощью команды быстрого меню (рисунок 6).

Java Eclipse. Команда запуска JUnit-тестаРисунок 6. Команда запуска JUnit-теста

После запуска в левой части Java Eclipse в окне JUnit отобразится результат тестирования (рисунок 7).

Java Eclipse. Результат JUnit-тестирования

Рисунок 7. Результат тестирования

Как видно из результата, получено подтверждение правильного решения, то есть тест сдан. Значит, метод Solution() класса SquareEquation дает правильный результат для случая когда a = 2, b = 1, c = -3.

Если в методе TestSolution() при вызове assertEquals() специально указать неверный ответ, например

assertEquals(rt.x2, 777.777);

то после повторного запуска теста, в окне JUnit отобразится ошибка как видно на рисунке 8. Хотя это не ошибка программы, а умышленная ошибка в тесте.

Java Eclipse. JUnit-тестирование. Демонстрация провала тестаРисунок 8. Демонстрация провала теста

 

4. Пример, демонстрирующий последовательность вызова методов в JUnit-тесте

Пусть в системе Java Eclipse создан класс с именем TestClass для проведения тестирования некоторого другого класса. В данном примере, суть тестируемого класса не имеет значения.

Программный код класса следующий

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class TestClass {

  @BeforeAll
  static void setUpBeforeClass() throws Exception {
    System.out.println("BeforeAll");
  }

  @BeforeAll
  static void setUpBeforeClass2() throws Exception {
    System.out.println("BeforeAll2");
  }

  @AfterAll
  static void tearDownAfterClass() throws Exception {
    System.out.println("AfterAll");
  }

  @BeforeEach
  void setUp() throws Exception {
    System.out.println("BeforeEach");
  }

  @BeforeEach
  void setUp2() throws Exception {
    System.out.println("BeforeEach2");
  }

  @AfterEach
  void tearDown() throws Exception {
    System.out.println("AfterEach");
  }

  @Test
  void testSolution() {
    System.out.println("Test");
  }

  @Test
  void testSolution2() {
    System.out.println("Test2");
  }
}

После запуска теста на выполнение, программа выдаст следующий результат

BeforeAll
BeforeAll2
BeforeEach2
BeforeEach
Test
AfterEach
BeforeEach2
BeforeEach
Test2
AfterEach
AfterAll

Проанализировав этот результат, можно прийти к выводу, что методы в тестовом классе вызываются в порядке, определяемом их аннотациями. Первыми вызываются методы аннотации @BeforeAll.

Следующими рассматриваются тестовые примеры, объявленные с аннотацией @Test. Перед каждым тестовым примером вызываются все методы с аннотацией @BeforeEach. После каждого тестового примера вызываются все методы с аннотацией @AfterEach.

Последними вызываются все методы с аннотацией @AfterAll.

 


Связанные темы