Я был поклонником написания юнит-тестов в течение достаточно долгого времени. Один из моих любимых примеров — когда я работал с командой разработчиков, переписывая процесс учета. Руководитель команды разбил нам работу, чтобы мы могли целенаправленно завершить работу. Проблема была в том, что каждому из нас требовалась работа от других разработчиков … работа, которая еще не была завершена.
После отображения наших ожиданий мы в конечном итоге использовали модульные тесты, фиктивные объекты и объекты-заглушки, чтобы имитировать входящие и исходящие данные для наших процессов. Это сработало довольно хорошо.
В этой короткой серии статей я планирую представить следующее:
-
Простое введение в юнит-тесты (эта статья)
-
Введение в макет и заглушки объектов
-
Различение юнит-тестов и интеграционных тестов
-
Заглядывая вперед
Поскольку у меня большой опыт работы с Java , я планирую сосредоточиться на языке программирования Java и инфраструктуре тестирования JUnit . Однако представленные концепции должны хорошо переводиться на другие объектно-ориентированные языки.
Простой пример — Candy Class
Для простоты рассмотрим следующий класс Candy:
public class Candy {
private static Double OUNCE_CONVERSION = new Double("0.035274");
private Integer id;
private String name;
private Integer size;
public Candy() {
}
public Candy(Integer id, String name, Integer size) {
this.id = id;
this.name = name;
this.size = size;
}
// getters and setters go here
public Double getSizeInOunces() {
if (this.size != null) {
return new Double(this.size * OUNCE_CONVERSION);
} else {
return new Double("0");
}
}
}
Объект Candy включает в себя идентификатор, имя и размер (который хранится в граммах). Вы заметите, что в этом классе есть вспомогательный метод. Хотя это не является обычной практикой, я добавил вспомогательный метод getSizeInOunces () для преобразования объекта размера из граммов в унции. Я также добавил статическую переменную Double, используемую для размещения множителя конверсии. Опять же, это просто для простоты.
Простой пример — класс CandyTest
Чтобы быть дисциплинированным разработчиком, необходимо создать модульное тестирование. Тест должен проверить объект может быть создан экземпляр, и мы можем проверить значения, хранящиеся в объекте. Поскольку у нас есть вспомогательный метод, тесты должны быть написаны для проверки результатов.
Используя JUnit, я установил некоторые статические переменные для размещения данных, используемых для тестирования объекта:
public static final Integer ID_ONE = new Integer("1");
public static final String NAME_GOOD_AND_PLENTY = new String("Good & Plenty");
public static final Integer SIZE_GRAMS_51 = new Integer("51");
public static final Double SIZE_GRAMS_51_IN_OUNCES = new Double("1.798974");
public static final Double SIZE_GRAMS_NULL_IN_OUNCES = new Double("0");
public static final Double SIZE_GRAMS_51_IN_OUNCES_INCORRECT = new Double("1.967527");
Эти переменные будут использоваться в тестах, чтобы избежать дублирования их в тестах.
Первый элемент, который мне нравится тестировать, — это тест «счастливый путь», где все работает как положено:
@Test
public void standardTest() {
Candy candy = new Candy(ID_ONE, NAME_GOOD_AND_PLENTY, SIZE_GRAMS_51);
assertTrue(candy.getId().equals(ID_ONE));
assertTrue(candy.getName().equals(NAME_GOOD_AND_PLENTY));
assertTrue(candy.getSize().equals(SIZE_GRAMS_51));
assertTrue(candy.getSizeInOunces().equals(SIZE_GRAMS_51_IN_OUNCES));
}
Тест устанавливает новый объект Candy с помощью пользовательского конструктора, а затем проверяет соответствие значений. Наконец, метод getSizeInOunces () проверяется на точность.
Далее создаются еще два теста. Первый из них устанавливает размер конфеты на ноль, а второй гарантирует, что тест подтвердит ложь, когда предлагается неправильное преобразование грамма в унцию:
@Test
public void nullSizeTest() {
Candy candy = new Candy();
candy.setId(ID_ONE);
candy.setName(NAME_GOOD_AND_PLENTY);
candy.setSize(null);
assertTrue(candy.getId().equals(ID_ONE));
assertTrue(candy.getName().equals(NAME_GOOD_AND_PLENTY));
assertTrue(candy.getSizeInOunces().equals(SIZE_GRAMS_NULL_IN_OUNCES));
}
@Test
public void incorrectConversionTest() {
Candy candy = new Candy(ID_ONE, NAME_GOOD_AND_PLENTY, SIZE_GRAMS_51);
assertTrue(candy.getId().equals(ID_ONE));
assertTrue(candy.getName().equals(NAME_GOOD_AND_PLENTY));
assertTrue(candy.getSize().equals(SIZE_GRAMS_51));
assertFalse(candy.getSizeInOunces().equals(SIZE_GRAMS_51_IN_OUNCES_INCORRECT));
}
Вы заметите, что метод nullSizeTest () использует методы конструктора и сеттера по умолчанию. Это было выбрано для достижения 100% покрытия кода.
Полный тестовый класс указан ниже:
public class CandyTest {
public static final Integer ID_ONE = new Integer("1");
public static final String NAME_GOOD_AND_PLENTY = new String("Good & Plenty");
public static final Integer SIZE_GRAMS_51 = new Integer("51");
public static final Double SIZE_GRAMS_51_IN_OUNCES = new Double("1.798974");
public static final Double SIZE_GRAMS_NULL_IN_OUNCES = new Double("0");
public static final Double SIZE_GRAMS_51_IN_OUNCES_INCORRECT = new Double("1.967527");
@Test
public void standardTest() {
Candy candy = new Candy(ID_ONE, NAME_GOOD_AND_PLENTY, SIZE_GRAMS_51);
assertTrue(candy.getId().equals(ID_ONE));
assertTrue(candy.getName().equals(NAME_GOOD_AND_PLENTY));
assertTrue(candy.getSize().equals(SIZE_GRAMS_51));
assertTrue(candy.getSizeInOunces().equals(SIZE_GRAMS_51_IN_OUNCES));
}
@Test
public void nullSizeTest() {
Candy candy = new Candy();
candy.setId(ID_ONE);
candy.setName(NAME_GOOD_AND_PLENTY);
candy.setSize(null);
assertTrue(candy.getId().equals(ID_ONE));
assertTrue(candy.getName().equals(NAME_GOOD_AND_PLENTY));
assertTrue(candy.getSizeInOunces().equals(SIZE_GRAMS_NULL_IN_OUNCES));
}
@Test
public void incorrectConversionTest() {
Candy candy = new Candy(ID_ONE, NAME_GOOD_AND_PLENTY, SIZE_GRAMS_51);
assertTrue(candy.getId().equals(ID_ONE));
assertTrue(candy.getName().equals(NAME_GOOD_AND_PLENTY));
assertTrue(candy.getSize().equals(SIZE_GRAMS_51));
assertFalse(candy.getSizeInOunces().equals(SIZE_GRAMS_51_IN_OUNCES_INCORRECT));
}
}
Простой пример — результаты CandyTest
Выполнение тестов с Clover Coverage в IntelliJ дает следующие результаты:
Все три теста прошли с зеленым значком ОК. Просмотр оригинального класса Candy также показывает 100% охват:
Вывод
В этом простом примере был создан новый класс, который включал простой расчет. Используя JUnit, был создан тестовый класс для проверки того, что класс Candy функционирует должным образом.
В следующей статье я представлю класс обслуживания Candy, который потребует использования фиктивных объектов для проверки логики обслуживания без необходимости полагаться на какие-либо внешние объекты или возможности подключения.
Хорошего дня!