Вы находитесь на странице: 1из 39

Модульное тестирование в Java

Сергей Довгалец
Unit testing
Модульное тестирование (unit testing,
юнит-тестирование) – вид тестирования,
направленный на оценку корректности
исходного кода программы.

Модульные тесты покрывают атомарные участки


кода, что позволяет удостовериться в их
работоспособности (в т.ч. после внесения изменений).
Особенности модульных тестов:
• Всегда должны проходить на 100%
• Отделены от кода приложения
• Независимы друг от друга, просты
• Часто пишутся программистами
• Могут писаться ДО кода приложения
Что проверяют модульные тесты:
Модульные Модульные
тесты тесты НЕ
тестируют тестируют

Высокоуровневую
Отдельные
логику
методы
приложения

Взаимодействие Пользовательский
объектов интерфейс
JUnit – фреймворк модульного тестирования
ПО на языке Java.

Имеет широкий набор расширений – таких как


JMock, HtmlUnit и т.д.

Портирован на другие языки: PHP, C#, Python,


Delphi, Perl, C++, JavaScript и т.д.
Что и как тестировать
Метод

Метод, который ничего


не возвращает

Класс

Взаимодействие
В рамках модульного классов
тестирования проверкам
подвергаются Геттеры и сеттеры

Конструкторы

Исключения

Внешние зависимости
Как тестировать…
• Метод: Вызвать метод, проверить возвращённый результат.
• Метод, который ничего не возвращает: вызвать метод, проверить «сторонние
изменения».
• Класс: определить ситуации, когда объект изменяет своё состояние, проверить
соответствующие сценарии(инициализация, выполнение методов и т.п.).
• Взаимодействие классов: определить ситуации, когда объекты взаимодействуют,
провести взаимодействие, проверить изменения в объектах.
• геттеры и сеттеры (НЕ НАДО тестировать простые get* и set*): вызвать сеттер,
вызвать геттер, проверить, что переданное в сеттер верно возвращено геттером.
• Конструктор: создать объект, проверить те изменения, которые производит
конструктор.
• Исключения: вызвать исключение, проверить факт его возникновения.
• Внешние зависимости: изолировать зависимость, вызвать метод, проверить
поведение объекта.
Понятие аннотации
Аннотации (annotations) – метаданные,
добавляемые в исходный код программы.

Не влияют на семантику кода, но


могут использоваться в процессе
анализа кода, компиляции и
выполнения программы.
Решаемые аннотациями задачи
Предоставление
информации
компилятору

Предоставление
метаданных
Решаемые задачи
инструментам генерации
кода, тестирования и т.п.

Использование в коде во
время выполнения
программы для принятия
различных решений
Аннотации в JUnit: fixtures
Фиксации (fixtures) – методы,
выполняющие подготовку к
выполнению тестового метода и
«уборку» после его выполнения.

Фиксация Фиксация
Аннотации в JUnit: fixtures

В JUnit есть две разновидности фиксаций,


которые помечаются следующими аннотациями:
• @Before / @After – выполняются до и после
КАЖДОГО тестового метода.
• @BeforeClass / @AfterClass – выполняются
до и после ВСЕГО НАБОРА тестовых
методов.
Аннотации в JUnit: fixtures
Аннотации в JUnit: fixtures

Аннотациями @Before / @After может быть


помечено НЕСКОЛЬКО тестовых методов.

Java гарантирует только то, что все @Before


выполнятся ДО тестового метода, а все
@After – после, но НЕ гарантирует порядок
выполнения самих @Before и @After.
Аннотации в JUnit: fixtures
@BeforeClass
public static void setUpBeforeClass() throws Exception { BeforeClass
System.out.println("BeforeClass");
} Before
@AfterClass
Test 1
public static void tearDownAfterClass() throws Exception { After
System.out.println("AfterClass");
} Before
@Before Test 2
public void setUp() throws Exception {
System.out.println("Before"); After
} Before
@After Test 3
public void tearDown() throws Exception {
System.out.println("After"); After
}
AfterClass
@Test
public void testOne()
{
System.out.println("Test 1");
}

@Test
public void testTwo()
{
System.out.println("Test 2");
}

@Test
public void testThree()
{
System.out.println("Test 3");
}
Аннотации в JUnit: tests

Тесты (tests) – методы, непосредственно


выполняющие проверку («тестовые
методы»).

Тестовые методы помечаются аннотацией


@Test.
@Test
public void tst_constructor()
{
assertEquals(1, cat.getType());
assertEquals(10.0, cat.getWeight(), 0.01);
}
Аннотации в JUnit – кратко
• @Before – выполняется перед каждым тестовым методом
• @After – выполняется после каждого тестового метода
• @BeforeClass – выполняется перед всем набором тестовых
методов
• @AfterClass – выполняется после всего набора тестовых
методов
• @Test – тестовый метод
• @Test(timeout=500) – тестовый метод с ограничением
времени выполнения
• @Test(expected=Exception.class) – тестовый метод,
ожидающий исключения
• @Ignore – тестовый метод, который не нужно выполнять
• @RunWith(value=Parameterized.class) – выполнение с
параметрами
• @RunWith(value=Suite.class) – выполнение набора тестов
@SuiteClasses(value={tstWaterSizzle.class,tstFireBurns.class}) –
набор тестов
Понятие проверок

Проверки (assertions) – специальные методы,


выполнение которых может закончиться успешно
(проверка прошла) или не успешно (проверка не
прошла).

В JUnit существует большое количество подобных


проверок:
http://kentbeck.github.com/junit/javadoc/latest/
Проверки – кратко:

• fail – остановка теста, генерация ошибки


• assertEquals – сравнение величин
• assertTrue – проверка на true
• assertFalse – проверка на false
• assertNull – проверка на null
• assertNotNull – проверка на НЕ null
• assertSame – проверка совпадения ссылок
• assertNotSame – проверка НЕ совпадения ссылок
• assertArrayEquals – сравнение массивов
• assertThat – сравнение с «выражением сравнения»
Аннотации в JUnit: tests + exceptions

Если тестовый метод ожидает


возникновения исключения, его аннотация
пишется так:
@Test(expected=ExpException.class)

Тестовый метод считается выполненным


успешно, если при его выполнении
ожидаемое исключение ВОЗНИКЛО.

@Test(expected=ArithmeticException.class)
public void tst_weight_compare()
{
Animal cat = new Animal(0, 0);
assertEquals(2.0, cat.weight_compare(ghost), 0.01);
}
Аннотации в JUnit: tests + timeouts

Если требуется, чтобы тестовый метод


выполнился за некоторое время,
аннотация оформляется так:
@Test(timeout=3000)

Тестовый метод считается


выполненным с ошибкой, если он
«не уложился» в указанное время.
@Test(timeout=2000)
public void tstTO()
{
assertEquals(expected_result, ip.increment(pass_value));
}
Аннотации в JUnit: tests + ignore

Если по какой-то причине в


настоящий момент тестовый метод
выполнять не нужно, его можно
дополнительно пометить
аннотацией @Ignore.

@Ignore("Not ready yet!")


@Test
public void tstIG()
{
assertEquals(expected_result, ip.increment(pass_value));
}
Аннотации в JUnit можно комбинировать

@Ignore("Just for fun...")


@Test(timeout=2000,expected=Exception.class)
public void tstIncrementParameterized2()
{
assertEquals(expected_result, ip.increment(pass_value));
}
Аннотации в JUnit: tests + parameters

Тестируемый метод:

public class IncrementParameterized {

public int increment(int a)


{
return ++a;
}

}
Аннотации в JUnit: tests + parameters
@RunWith(value = Parameterized.class)
public class tstIncrementParameterized {

private int pass_value;


private int expected_result;
private IncrementParameterized ip;

@Parameterized.Parameters
public static Collection set_of_parameters() {
return Arrays.asList(new Object[][]{
{3, 2},
{4, 3}
}
);
}

public tstIncrementParameterized(int expected_result, int pass_value) {


this.expected_result = expected_result;
this.pass_value = pass_value;
}

@Before
public void setUp() {
ip = new IncrementParameterized();
}

@Test
public void tstIncrementParameterized() {
assertEquals(expected_result, ip.increment(pass_value));
}
}
Аннотации в JUnit: suites

У нас есть тесты:

Нужно выполнить только


tstWaterSizzle.java
и
tstFireBurns.java
Аннотации в JUnit: suites

Создадим тест-сьют (набор тестов)


_tstRunner()
@RunWith(value=Suite.class)
@SuiteClasses(value={tstWaterSizzle.class,tstFireBurns.class})
public class _tstRunner {

1. Тесты выполняются в том порядке, в каком


перечислены.
2. В JUnit 4 НЕЛЬЗЯ включить в набор отдельные
тестовые методы.
Остановка теста: fail
fail() – это «проверка», которая всегда
заканчивается неудачей. Данный метод генерирует
ошибку выполнения теста (завершает тест
ошибкой) с выводом сообщения или без вывода.
@Test
public void tstAllAsserts()
{
fail();
fail("Test failed!");
}

Часто используется как «заглушка» для тестов,


логика которых ещё не реализована – чтобы не
забыть о таком тесте (он всегда будет
«заваливаться»).
Сравнение: assertEquals

assertEquals() сравнивает два значения. Проверка


считается пройденной, если значения равны.

@Test
public void tstAllAsserts()
{
assertEquals(4.0, 2.0*2.0, 0.0001);
assertEquals("Err!", 4.0, 2.0*2.0, 0.0001);
assertEquals(4, 2*2);
}
Истинность: assertTrue

assertTrue() проверяет, является ли переданный


аргумент логически равным true. Проверка
считается пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertTrue(some_bool_var);
assertTrue(2*2 == 4);
assertTrue("Math error!", 2*2 == 4);
}
Истинность: assertFalse

assertFalse() проверяет, является ли переданный


аргумент логически равным false. Проверка
считается пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertFalse(some_bool_var);
assertFalse(2*3 == 4);
assertFalse("Math OK!", 2*3 == 4);
}
Проверка отсутствия: assertNull

assertNull() проверяет, является ли переданный


аргумент логически равным null (т.е.
«отсутствиующим»). Проверка считается
пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertNull(obj);
assertNull("Self-created obj?", obj);
}
Проверка присутствия: assertNotNull

assertNotNull() проверяет, является ли переданный


аргумент логически НЕ равным null. Проверка
считается пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertNotNull(obj);
assertNotNull("Creation failed!?", obj);
}
Сравнение объектов: assertSame

assertSame() проверяет, ссылаются ли переданные


аргументы на один и тот же объект. Проверка
считается пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertSame(obj1, obj2);
assertSame("Different!", obj1, obj2);
}
Сравнение объектов: assertNotSame

assertNotSame() проверяет, ссылаются ли


переданные аргументы на разные объекты.
Проверка считается пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertNotSame(obj1, obj2);
assertNotSame("The same!", obj1, obj2);
}
Сравнение массивов: assertArrayEquals

assertArrayEquals() проверяет, являются ли


массивы идентичными. Проверка считается
пройденной, если это так.
@Test
public void tstAllAsserts()
{
assertArrayEquals(int_arr1, int_arr2);
assertArrayEquals("NO!", sa1, sa2);
}
Выражение сравнения: assertThat
assertThat() проводит определение истинности
«выражения сравнения». Проверка считается
пройденной, если выражение истинно. Требует
подключения
import static org.hamcrest.CoreMatchers.*;

Хорошее пояснение есть здесь: http://junit.sourceforge.net/doc/ReleaseNotes


4.4.html
и здесь:
http://kentbeck.github.com/junit/javadoc/latest/org/hamcrest/CoreMatchers.html

@Test
public void tstAllAsserts()
{
assertThat(0, is(1));
assertThat(0, is(not(1)));
}
Сценарии в JUnit

Сценарии в JUnit реализуются двумя способами:


• Тестовыми методами внутри одного класса.
• Несколькими классами, объединёнными в тест-
сьют (тестовый набор, тестовый сценарий).
В обоих случаях составные элементы сценария
должны быть независимы друг от друга.

Для упрощения обеспечения независимости можно


использовать @Before и @After.
Пример реализации независимости
public class tstShowIndependence {

Animal cat;
Animal mouse;

@Before
public void createCreatures() throws Exception {
cat = new Animal(1, 10);
mouse = new Animal(2, 1);
cat.born();
mouse.born();
}

@Test
public void tstCatEatMouse()
{
assertEquals(0.5, cat.eat(mouse, mouse.getWeight()), 0.01);
}

@Test
public void tstMouseEatCat()
{
assertEquals(0.5, mouse.eat(cat, cat.getWeight()), 0.01);
}

@After
public void killCreatures() throws Exception {
cat.die();
mouse.die();
cat = null;
mouse = null;
}
}
Практическое занятие
Написать к своим
программам модульные
тесты.

Например: Для
тестирования написать игру
в «города» (человек и
компьютер по очереди
называют «город на
последнюю букву»,
например «Москва –
Астрахань – Новосибирск –
Киев – Вильнюс»).