1. Обзор
Среды запуска тестов, такие как JUnit и TestNG , предоставляют некоторые базовые методы утверждения ( assertTrue
, assertNotNull
и т. д .).
Кроме того, существуют фреймворки утверждений, такие как Hamcrest , AssertJ и Truth , которые предоставляют плавные и богатые методы утверждений с именами, которые обычно начинаются с «assertThat»
.
JSpec — это еще один фреймворк, который позволяет нам писать утверждения более плавно, почти так же, как мы пишем спецификации на нашем естественном языке , хотя и немного иначе, чем в других фреймворках.
В этой статье мы узнаем, как использовать JSpec. Мы продемонстрируем методы, необходимые для написания наших спецификаций, и сообщения, которые будут напечатаны в случае сбоя теста.
2. Зависимости Maven
Давайте импортируем зависимость javalite-common
, содержащую JSpec:
<dependency>
<groupId>org.javalite</groupId>
<artifactId>javalite-common</artifactId>
<version>1.4.13</version>
</dependency>
Для получения последней версии проверьте репозиторий Maven Central .
3. Сравнение стилей утверждений
Вместо типичного способа утверждения, основанного на правилах, мы просто пишем спецификацию поведения. Давайте рассмотрим быстрый пример подтверждения равенства в JUnit, AssertJ и JSpec.
В JUnit мы бы написали:
assertEquals(1 + 1, 2);
А в AssertJ мы бы написали:
assertThat(1 + 1).isEqualTo(2);
Вот как мы напишем тот же тест в JSpec:
$(1 + 1).shouldEqual(2);
JSpec использует тот же стиль, что и фреймворки с плавными утверждениями, но пропускает ведущее ключевое слово assert
/ assertThat
и вместо этого использует should .
Написание утверждений таким образом упрощает представление реальных спецификаций , продвигая концепции TDD и BDD.
Посмотрите, как этот пример очень близок к нашему естественному написанию спецификаций:
String message = "Welcome to JSpec demo";
the(message).shouldNotBe("empty");
the(message).shouldContain("JSpec");
4. Структура спецификаций
Заявление о спецификации состоит из двух частей: создателя ожидания и метода ожидания.
4.1. Создатель ожиданий
Создатель ожиданий генерирует объект Expectation
, используя один из этих статически импортированных методов: a()
, the()
, it()
, $():
$(1 + 2).shouldEqual(3);
a(1 + 2).shouldEqual(3);
the(1 + 2).shouldEqual(3);
it(1 + 2).shouldEqual(3);
Все эти методы по существу одинаковы — все они существуют только для предоставления различных способов выражения нашей спецификации.
Единственное отличие состоит в том, что метод it()
безопасен для типов , что позволяет сравнивать только объекты одного типа:
it(1 + 2).shouldEqual("3");
Сравнение объектов разных типов с помощью it()
приведет к ошибке компиляции.
4.2. Метод ожидания
Вторая часть оператора спецификации — это метод ожидания, который сообщает о требуемой спецификации , например , shouldEqual
, shouldContain
.
Когда тест не пройден, исключение типа javalite.test.jspec.TestException
отображает выразительное сообщение. Мы увидим примеры таких сообщений об ошибках в следующих разделах.
5. Встроенные ожидания
JSpec предоставляет несколько видов методов ожидания. Давайте рассмотрим их, в том числе сценарий для каждого из них, который показывает сообщение об ошибке, которое JSpec генерирует при сбое теста.
5.1. Ожидание равенства
должно быть равно (), должно быть равно (), должно быть не равно ()
Они указывают, что два объекта должны/не должны быть равны, используя метод java.lang.Object.equals()
для проверки на равенство:
$(1 + 2).shouldEqual(3);
Сценарий отказа:
$(1 + 2).shouldEqual(4);
выдаст следующее сообщение:
Test object:java.lang.Integer == <3>
and expected java.lang.Integer == <4>
are not equal, but they should be.
5.2. Ожидание логического свойства
должен иметь(), должен не иметь()
Мы используем эти методы, чтобы указать , должно ли именованное логическое
свойство объекта возвращать true
:
Cage cage = new Cage();
cage.put(tomCat, boltDog);
the(cage).shouldHave("animals");
Для этого требуется, чтобы класс Cage
содержал метод с сигнатурой:
boolean hasAnimals() {...}
Сценарий отказа:
the(cage).shouldNotHave("animals");
выдаст следующее сообщение:
Method: hasAnimals should return false, but returned true
должно быть(), не должно быть()
Мы используем их, чтобы указать, что тестируемый объект должен/не должен быть чем-то:
the(cage).shouldNotBe("empty");
Для этого требуется, чтобы класс Cage
содержал метод с сигнатурой «boolean isEmpty()».
Сценарий отказа:
the(cage).shouldBe("empty");
выдаст следующее сообщение:
Method: isEmpty should return true, but returned false
5.3. Ожидание типа
долженBeType(), долженBeA()
Мы можем использовать эти методы, чтобы указать, что объект должен быть определенного типа:
cage.put(boltDog);
Animal releasedAnimal = cage.release(boltDog);
the(releasedAnimal).shouldBeA(Dog.class);
Сценарий отказа:
the(releasedAnimal).shouldBeA(Cat.class);
выдаст следующее сообщение:
class com.foreach.jspec.Dog is not class com.foreach.jspec.Cat
5.4. Ожидание обнуления
должно быть пустым (), должно быть не пустым ()
Мы используем их, чтобы указать, что тестируемый объект должен/не должен быть нулевым
:
cage.put(boltDog);
Animal releasedAnimal = cage.release(dogY);
the(releasedAnimal).shouldBeNull();
Сценарий отказа:
the(releasedAnimal).shouldNotBeNull();
выдаст следующее сообщение:
Object is null, while it is not expected
5.5. Эталонное ожидание
должно быть тем же, что и(), должно быть не тем же, что и()
Эти методы используются для указания того, что ссылка на объект должна быть такой же, как и ожидаемая:
Dog firstDog = new Dog("Rex");
Dog secondDog = new Dog("Rex");
$(firstDog).shouldEqual(secondDog);
$(firstDog).shouldNotBeTheSameAs(secondDog);
Сценарий отказа:
$(firstDog).shouldBeTheSameAs(secondDog);
выдаст следующее сообщение:
references are not the same, but they should be
5.6. Коллекция и ожидаемое содержимое строки
shouldContain(), shouldNotContain()
Мы используем их, чтобы указать, что тестируемая коллекция
или карта
должны/не должны содержать данный элемент:
cage.put(tomCat, felixCat);
the(cage.getAnimals()).shouldContain(tomCat);
the(cage.getAnimals()).shouldNotContain(boltDog);
Сценарий отказа:
the(animals).shouldContain(boltDog);
выдаст следующее сообщение:
tested value does not contain expected value: Dog [name=Bolt]
Мы также можем использовать эти методы, чтобы указать, что строка
должна/не должна содержать заданную подстроку:
$("Welcome to JSpec demo").shouldContain("JSpec");
И хотя это может показаться странным, мы можем распространить это поведение на другие типы объектов, которые сравниваются с помощью их методов toString()
:
cage.put(tomCat, felixCat);
the(cage).shouldContain(tomCat);
the(cage).shouldNotContain(boltDog);
Чтобы уточнить, метод toString ()
объекта Cat
tomCat
будет производить:
Cat [name=Tom]
которая является подстрокой вывода toString() объекта
клетки
:
Cage [animals=[Cat [name=Tom], Cat[name=Felix]]]
6. Пользовательские ожидания
Помимо встроенных ожиданий, JSpec позволяет нам писать собственные ожидания.
6.1. Разница Ожидание
Мы можем написать DifferenceExpectation
, чтобы указать, что возвращаемое значение выполнения некоторого кода не должно быть равно определенному значению.
В этом простом примере мы убеждаемся, что операция (2 + 3) не даст нам результат (4):
expect(new DifferenceExpectation<Integer>(4) {
@Override
public Integer exec() {
return 2 + 3;
}
});
Мы также можем использовать его, чтобы гарантировать, что выполнение некоторого кода изменит состояние или значение некоторой переменной или метода.
Например, при выпуске животного из клетки
, содержащей двух животных, размер должен быть другим:
cage.put(tomCat, boltDog);
expect(new DifferenceExpectation<Integer>(cage.size()) {
@Override
public Integer exec() {
cage.release(tomCat);
return cage.size();
}
});
Сценарий отказа:
Здесь мы пытаемся выпустить животное, которого нет в клетке
:
cage.release(felixCat);
Размер не изменится, и мы получим следующее сообщение:
Objects: '2' and '2' are equal, but they should not be
6.2. Ожидание исключения
Мы можем написать ExceptionExpectation
, чтобы указать, что тестируемый код должен генерировать Exception
.
Мы просто передадим конструктору ожидаемый тип исключения и предоставим его как общий тип:
expect(new ExceptionExpectation<ArithmeticException>(ArithmeticException.class) {
@Override
public void exec() throws ArithmeticException {
System.out.println(1 / 0);
}
});
Сценарий отказа №1:
System.out.println(1 / 1);
Поскольку эта строка не приведет к исключению, ее выполнение приведет к следующему сообщению:
Expected exception: class java.lang.ArithmeticException, but instead got nothing
Сценарий отказа №2:
Integer.parseInt("x");
Это приведет к исключению, отличному от ожидаемого исключения:
class java.lang.ArithmeticException,
but instead got: java.lang.NumberFormatException: For input string: "x"
7. Заключение
Другие фреймворки с плавными утверждениями предоставляют лучшие методы для утверждения коллекций, утверждения исключений и интеграции с Java 8, но JSpec предоставляет уникальный способ написания утверждений в форме спецификаций.
У него есть простой API, который позволяет нам писать наши утверждения как на естественном языке, и он предоставляет описательные сообщения об ошибках теста.
Полный исходный код всех этих примеров можно найти на GitHub — в пакете com.foreach.jspec
.