«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 использует тот же стиль, что и фреймворки Fluent Assert, но опускает начальное ключевое слово 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. Ожидание равенства
shouldEqual(), shouldBeEqual(), shouldNotBeEqual()
Они указывают, что два объекта должны/не должны быть равны, используя метод 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. Логическое ожидание свойства
shouldHave(), shouldNotHave()
Мы используем эти методы, чтобы указать, должно ли именованное логическое свойство объекта возвращать значение 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
shouldBe(), shouldNotBe()
Мы используем их, чтобы указать, что тестируемый объект должен/не должен быть чем-то:
the(cage).shouldNotBe("empty");
Это требует, чтобы класс Cage содержал метод с сигнатурой «boolean isEmpty()».
Сценарий отказа:
the(cage).shouldBe("empty");
выдаст следующее сообщение:
Method: isEmpty should return true, but returned false
5.3. Ожидание типа
shouldBeType(), shouldBeA()
Мы можем использовать эти методы, чтобы указать, что объект должен быть определенного типа:
cage.put(boltDog);
Animal releasedAnimal = cage.release(boltDog);
the(releasedAnimal).shouldBeA(Dog.class);
Сценарий отказа:
the(releasedAnimal).shouldBeA(Cat.class);
приведет к следующее сообщение:
class com.baeldung.jspec.Dog is not class com.baeldung.jspec.Cat
5.4. Ожидание обнуляемости
«shouldBeNull(), shouldNotBeNull()
Мы используем их, чтобы указать, что тестируемый объект должен/не должен быть нулевым:
cage.put(boltDog);
Animal releasedAnimal = cage.release(dogY);
the(releasedAnimal).shouldBeNull();
Сценарий отказа:
the(releasedAnimal).shouldNotBeNull();
выдаст следующее сообщение: ~~ ~
Object is null, while it is not expected
5.5. Ожидание ссылки
shouldBeTheSameAs(), shouldNotBeTheSameAs()
Эти методы используются для указания того, что ссылка на объект должна совпадать с ожидаемой:
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 выдаст: вывод toString() объекта клетки:
Cat [name=Tom]
6. Пользовательские ожидания
Cage [animals=[Cat [name=Tom], Cat[name=Felix]]]
Помимо встроенных ожиданий, JSpec позволяет нам записывать пользовательские ожидания.
6.1. DifferenceExpectation
Мы можем написать 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);
6.2. Ожидание исключения
Objects: '2' and '2' are equal, but they should not be
Мы можем написать ExceptionExpectation, чтобы указать, что тестируемый код должен вызывать исключение.
Мы просто передадим ожидаемый тип исключения в конструктор и предоставим его как общий тип:
Сценарий ошибки №1:
expect(new ExceptionExpectation<ArithmeticException>(ArithmeticException.class) {
@Override
public void exec() throws ArithmeticException {
System.out.println(1 / 0);
}
});
Так как эта строка не приведет ни к какому исключению , его выполнение выдаст следующее сообщение:
System.out.println(1 / 1);
Сценарий сбоя #2:
Expected exception: class java.lang.ArithmeticException, but instead got nothing
Это приведет к исключению, отличному от ожидаемого исключения:
Integer.parseInt("x");
7. Заключение
class java.lang.ArithmeticException,
but instead got: java.lang.NumberFormatException: For input string: "x"
Другие фреймворки fluent-утверждений предоставляют лучшие методы для утверждений коллекций, утверждений исключений и интеграции с Java 8, но JSpec предоставляет уникальный способ написания утверждений в форме спецификаций.
У него есть простой API, который позволяет нам писать наши утверждения на естественном языке, и он предоставляет описательные сообщения об ошибках теста.
Полный исходный код всех этих примеров можно найти на GitHub — в пакете com.baeldung.jspec.
«