Писмени тестове
Писането на тестове за Nette Tester е уникално с това, че всеки тест е PHP скрипт, който може да се изпълнява отделно. Това има голям потенциал. След като сте написали тест, можете просто да го стартирате, за да проверите дали работи правилно. Ако това не е така, можете лесно да преминете през теста в IDE и да потърсите грешка.
Можете дори да отворите теста в браузър. Но най-важното е, че след като го стартирате, стартирате теста. Веднага ще разберете дали е преминал успешно или не.
В уводната глава показахме един наистина тривиален тест за масиви на PHP. Сега ще създадем наш собствен клас, който ще тестваме, въпреки че той също е прост.
Ще започнем с типична схема на директория на библиотека или проект. Важно е да се отделят тестовете от останалата част от кода, например заради внедряването, тъй като не искаме да качваме тестовете на сървъра. Структурата може да бъде следната:
├── src/ # code that we will test
│ ├── Rectangle.php
│ └── ...
├── tests/ # tests
│ ├── bootstrap.php
│ ├── RectangleTest.php
│ └── ...
├── vendor/
└── composer.json
Сега ще създадем отделни файлове. Нека започнем с тествания клас,
който ще поставим във файла src/Rectangle.php
И създайте тест за него. Името на тестовия файл трябва да съответства
на маската *Test.php
или *.phpt
, ние ще изберем
RectangleTest.php
:
Както можете да видите, методите за утвърждаване,
като например Assert::same()
, се използват за утвърждаване, че
действителната стойност съвпада с очакваната стойност.
Последната стъпка е да се създаде файлът bootstrap.php
. Тя съдържа
общ код за всички тестове. Например, автоматично зареждане на класове,
конфигурация на средата, създаване на временна директория, помощни
програми и други подобни. Всеки тест зарежда bootstrap и се фокусира само
върху тестването. Един bootstrap може да изглежда по следния начин:
Този bootstrap предполага, че автозареждащият модул на Composer също
ще може да зареди класа Rectangle.php
. Това може да се постигне
например чрез задаване на секцията за
автоматично зареждане на composer.json
, и т.н.
Сега можем да стартираме теста от командния ред като всеки друг PHP скрипт. Първото стартиране ще открие всички синтактични грешки и ако не сте допуснали никакви грешки, ще видите
$ php RectangleTest.php
OK
Ако променим твърдението в теста на false Assert::same(123, $rect->getArea());
,
ще се случи следното:
$ php RectangleTest.php Failed: 200.0 should be 123 in RectangleTest.php(5) Assert::same(123, $rect->getArea()); FAILURE
Когато пишете тестове, е полезно да уловите всички екстремни ситуации. Например, ако входът е нула, отрицателно число, в други случаи – празен низ, нула и т.н. Всъщност тя ви принуждава да мислите и да решавате как да се държи кодът в такива ситуации. След това тестовете коригират поведението.
В нашия случай отрицателната стойност трябва да предизвика изключение, което проверяваме с Assert::exception():
Добавяме и подобен тест за височина. Накрая проверяваме дали
isSquare()
връща true
, ако двете измервания са еднакви. Опитайте
се да напишете такива тестове като упражнение.
Добре организирани тестове
Размерът на тестовия файл може да се увеличи и бързо да стане претрупан. Поради това е препоръчително да се групират отделните области на изпитване в отделни функции.
Първо ще покажем по-прост, но по-елегантен вариант, като използваме
глобалната функция test()
. Tester не я създава автоматично, за да
избегне колизия, ако в кода ви има функция със същото име. Той се
създава само чрез метода setupFunctions()
, който се извиква във файла
bootstrap.php
:
С помощта на тази функция можем да разделим тестовия файл на именувани блокове. Когато функцията се изпълни, етикетите ще се показват един по един.
Ако трябва да стартирате код преди или след всеки тест, предайте го на
setUp()
или tearDown()
:
Второй вариант – объектный. Мы создадим так называемый TestCase, который представляет собой класс, где отдельные единицы представлены методами, имена которых начинаются с test-.
На этот раз мы использовали аннотацию @throw
для проверки на
исключения. Более подробную информацию смотрите в главе TestCase.
Функции-помощники
Nette Tester включает в себя несколько классов и функций, которые могут облегчить вам тестирование, например, помощники для тестирования содержимого HTML-документа, для тестирования функций работы с файлами и так далее.
Их описание вы можете найти на странице Helpers.
Аннотирование и пропуск тестов
На выполнение тестов могут влиять аннотации в комментарии phpDoc в начале файла. Например, он может выглядеть следующим образом:
Аннотации гласят, что тест должен выполняться только с PHP версии
7.2 или выше и при наличии PHP расширений pdo и pdo_pgsql. Эти аннотации
контролируются программой запуска тестов командной
строки, которая, если условия не выполняются, пропускает тест и
помечает его буквой s
– пропущен. Однако они не имеют никакого
эффекта, когда тест выполняется вручную.
Описание аннотаций приведено в разделе Аннотации тестов.
Тест также может быть пропущен на основании собственного условия с
помощью Environment::skip()
. Например, мы пропустим этот тест на Windows:
Структура каталогов
Для немного больших библиотек или проектов мы рекомендуем разделить тестовый каталог на подкаталоги в соответствии с пространством имен тестируемого класса:
└── тестове/.
├── NamespaceOne/
│ ├── MyClass.getUsers.phpt
│ ├── MyClass.setUsers.phpt
│ └── ...
│
├── NamespaceTwo/
│ ├── MyClass.creating.phpt
│ ├── MyClass.dropping.phpt
│ └── ...
│
├── bootstrap.php
└── ...
Ще можете да стартирате тестове от същото пространство от имена, т.е. от поддиректория:
tester tests/NamespaceOne
Крайни случаи
Тест, който не извиква нито един метод за утвърждаване, е подозрителен и ще бъде оценен като грешен:
Error: This test forgets to execute an assertion.
Ако един тест без извикване на твърдения наистина трябва да се счита
за правилен, извикайте например Assert::true(true)
.
Коварно може да бъде и използването на exit()
и die()
, за да
се завърши тестът със съобщение за грешка. Например
exit('Error in connection')
завършва теста с код на излизане 0, което е сигнал
за успех. Използвайте Assert::fail('Error in connection')
.