Здравствуйте.
На написание статьи меня сподвигнул этот пост. В нём приведено описание инструментов и некоторая теоретическая информация.
Сам я только начинаю разбираться в unit-тестировании и тестировании вообще, поэтому решил поделиться некоторой информацией касательно этого дела. А также систематизировать свои знания и навыки. Далее постараюсь объяснить процесс тестирования по шагам простым обывательским языком, так как нигде в интернете не нашёл разжёванного описания, по шагам так сказать. Кому интересно и кто хочет попробовать всё-таки разобраться, добро пожаловать.
Что такое автоматизированное тестирование и unit-тестирование я писать не буду, для этого есть википедия.
Для наших тестов будем использовать, наверное самый популярный фрэймворк – PHPUnit. Для начала нам необходимо его утановить. Делать это проще всего через PEAR. Как это сделать, написано в документации. Используется две команды(из документации):
Естественно, путь к PEAR должен быть прописан в PATH. Когда загрузятся необходимые файлы, наш PHPUnit будет полностью готов к тестированию нашего кода.
Итак, начнём. Пусть у нас будет какая-то модель данных. В ней два атрибута: строка и число. Есть метод-сеттер и методы для сохранения и загрузки значений (в файл).
TestModel.php
Мы определили базовые методы и атрибуты классов. Так как у нас пока ничего не читается и не пишется, по условию возвращаем false.
Введём некоторые искуственные ограничения для аттрибутов:
Конечно, в реальных проектах ограничений больше, но для начала нам хватит :)
Теперь отложим на время нашу модель и займёмся тестом. Тест представляет собой обычный класс, унаследованный от базового класса (в нашем случае PHPUnit_Framework_TestCase). Методы этого класса, и есть тесты. Создадим папку unit для нашего теста.
unit/TestModelTest.php:
TestModelTest — наш тест-класс для класса TestModel.
testTrue() — непосредственно тест. В нём мы определяем сценарии для конкретных случаев. В данном тесте мы просто проверим, что true является true :) Это делается при помощи метода assertTrue (assert-англ-утверждать). Т.е. мы утверждаем, что true является истинной.
Запустим наш тест. PHPUnit достаточно указать папку, в которой лежат все наши тесты.
Получаем:
Ура, наш тест работает! Идём далее.
TDD – Test Driven Development – подхо��, при котором, грубо говоря, сначала пишутся тесты, а потом постепенно, исходя из них, пишется основной класс. Подробнее в википедии. Пойдём этим путём. Каркас модуля у нас уже есть. Требования тоже. Теперь напишем тестовые случаи, исходя из наших требований.
unit/TestModelTest.php:
Мы описали все три случая в трёх методах. Для каждого свой. Теперь запустим тесты:
Damn! Ну ничего, так и должно быть :) Теперь добавим немного кода в нашу модель.
unit/TestModelTest.php:
Думаю, в коде ничего не должно вызывать затруднений.
Запускаем тесты:
Уже лучше. Уже проходит в два раза больше проверок. Идём по порядку:
1. testStringCannotBeEmpty. Строка не может быть пустой. Добавляем проверку:
2. testIntMustBeGreaterThanTenAdnSmallerThanTwenty. Условие 10<x<20. Проверка:
3. testSaveLoad. Ага! ещё одна ошибка, на первый взгляд её сложно заметить. Строка записанная не равна строке прочитанной. Всё дело в конце строки. Идём в документацию и читаем или узнаём про флаг FILE_IGNORE_NEW_LINES.
Исправляем.
(spoiler: условие 2 специально не соблюдено)
Запустим:
Смотрим на 46 строчку (у меня): $model->setAttributes(20,'test3'); Мы не рассмотрели крайний случай! Исправляем:
Запускаем наши тесты:
Ура, все три теста прошли. Наша модель удовлетворяет поставленным требованиям. Что и требовалось :)
Эта статья ни в коей мере не претендует на полное руководство по unit-тестированию, ни тем более на руководст��о по TDD. Цель данной статьи — в первую очередь систематизировать мои(начинающего) познания в данной сфере. И я очень надеюсь, что она поможет кому-нибудь в качестве начального подспорья для погружения в глубокий мир автоматического тестирования.
Спасибо за внимание.
На написание статьи меня сподвигнул этот пост. В нём приведено описание инструментов и некоторая теоретическая информация.
Сам я только начинаю разбираться в unit-тестировании и тестировании вообще, поэтому решил поделиться некоторой информацией касательно этого дела. А также систематизировать свои знания и навыки. Далее постараюсь объяснить процесс тестирования по шагам простым обывательским языком, так как нигде в интернете не нашёл разжёванного описания, по шагам так сказать. Кому интересно и кто хочет попробовать всё-таки разобраться, добро пожаловать.
Что такое автоматизированное тестирование и unit-тестирование я писать не буду, для этого есть википедия.
Для наших тестов будем использовать, наверное самый популярный фрэймворк – PHPUnit. Для начала нам необходимо его утановить. Делать это проще всего через PEAR. Как это сделать, написано в документации. Используется две команды(из документации):
pear config-set auto_discover 1 pear install pear.phpunit.de/PHPUnit
Естественно, путь к PEAR должен быть прописан в PATH. Когда загрузятся необходимые файлы, наш PHPUnit будет полностью готов к тестированию нашего кода.
Let's Rock
Итак, начнём. Пусть у нас будет какая-то модель данных. В ней два атрибута: строка и число. Есть метод-сеттер и методы для сохранения и загрузки значений (в файл).
TestModel.php
class TestModel { public $num; public $str; public function setAttributes($i, $s) {} /* @return: true, если данные сохран��ны false, в обратном случае */ public function saveData() {return false;} /* @return: true, если данные успешно прочитаны из файла false, в обратном случае */ public function loadData() {return false;} }
Мы определили базовые методы и атрибуты классов. Так как у нас пока ничего не читается и не пишется, по условию возвращаем false.
Введём некоторые искуственные ограничения для аттрибутов:
- Строка не может быть пустой
- Число должно быть больше 10, но меньше 20
- Естественно, данные должны правильно заноситься и в файл и читаться оттуда
Конечно, в реальных проектах ограничений больше, но для начала нам хватит :)
Теперь отложим на время нашу модель и займёмся тестом. Тест представляет собой обычный класс, унаследованный от базового класса (в нашем случае PHPUnit_Framework_TestCase). Методы этого класса, и есть тесты. Создадим папку unit для нашего теста.
unit/TestModelTest.php:
require_once 'PHPUnit/Autoload.php'; class TestModelTest extends PHPUnit_Framework_TestCase { function testTrue() { $this->assertTrue(true); } }
TestModelTest — наш тест-класс для класса TestModel.
testTrue() — непосредственно тест. В нём мы определяем сценарии для конкретных случаев. В данном тесте мы просто проверим, что true является true :) Это делается при помощи метода assertTrue (assert-англ-утверждать). Т.е. мы утверждаем, что true является истинной.
Запустим наш тест. PHPUnit достаточно указать папку, в которой лежат все наши тесты.
phpunit unit
Получаем:
PHPUnit 3.6.10 by Sebastian Bergmann. . Time: 0 seconds, Memory: 2.75Mb OK (1 test, 1 assertion)
Ура, наш тест работает! Идём далее.
TDD
TDD – Test Driven Development – подхо��, при котором, грубо говоря, сначала пишутся тесты, а потом постепенно, исходя из них, пишется основной класс. Подробнее в википедии. Пойдём этим путём. Каркас модуля у нас уже есть. Требования тоже. Теперь напишем тестовые случаи, исходя из наших требований.
unit/TestModelTest.php:
<?php require_once 'PHPUnit/Autoload.php'; require_once dirname(__FILE__).'/../TestModel.php'; class TestModelTest extends PHPUnit_Framework_TestCase { //проверяем условие, что строка не может быть пустой function testStringCannotBeEmpty() { $model=new TestModel; $model->setAttributes(15,''); $this->assertFalse($model->saveData()); //мы утверждаем, что на выходе должна быть ложь! $model->setAttributes(15,'aaaa'); $this->assertTrue($model->saveData()); //а теперь истина } //проверяем условие 10<i<20 function testIntMustBeGreaterThanTenAdnSmallerThanTwenty() { $model=new TestModel; /* Условия ложны */ $model->setAttributes(2,'test1'); $this->assertFalse($model->saveData()); $model->setAttributes(10,'test2'); $this->assertFalse($model->saveData()); $model->setAttributes(20,'test3'); $this->assertFalse($model->saveData()); $model->setAttributes(25,'test4'); $this->assertFalse($model->saveData()); /* Условие истинно */ $model->setAttributes(15,'test5'); $this->assertTrue($model->saveData()); } //проверяем корректность чтения/записи function testSaveLoad() { $i=13; $str='test'; $model=new TestModel; $model->setAttributes($i,$str); $this->assertTrue($model->saveData()); //записали данные $fetchModel=new TestModel; $this->assertTrue($fetchModel->loadData()); //прочитали данные //сравниваем прочитанные данные и исходные $this->assertEquals($fetchModel->num,$i); $this->assertEquals($fetchModel->str,$str); } }
Мы описали все три случая в трёх методах. Для каждого свой. Теперь запустим тесты:
PHPUnit 3.6.10 by Sebastian Bergmann. FFF Time: 0 seconds, Memory: 2.75Mb There were 3 failures: 1) TestModelTest::testStringCannotBeEmpty Failed asserting that null is false. ... 2) TestModelTest::testIntMustBeGreaterThanTenAdnSmallerThanTwenty Failed asserting that null is false. ... 3) TestModelTest::testSaveLoad Failed asserting that null is true. ... FAILURES! Tests: 3, Assertions: 3, Failures: 3.
Damn! Ну ничего, так и должно быть :) Теперь добавим немного кода в нашу модель.
unit/TestModelTest.php:
class TestModel { public $num; public $str; public $fname="file.txt"; public function setAttributes($i, $s) { $this->num=(int)$i; $this->str=$s; } public function saveData() { $h=fopen($this->fname,'w+'); $res=fputs($h, $this->str."\r\n".$this->num); fclose($h); return (bool)$res; } public function loadData() { $arr=file($this->fname); if ($arr==false) return false; list($this->str,$this->num)=$arr; return (bool)$arr; } }
Думаю, в коде ничего не должно вызывать затруднений.
Запускаем тесты:
There were 3 failures: 1) TestModelTest::testStringCannotBeEmpty Failed asserting that true is false. ... 2) TestModelTest::testIntMustBeGreaterThanTenAdnSmallerThanTwenty Failed asserting that true is false. ... 3) TestModelTest::testSaveLoad Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'test - -' +'test' FAILURES! Tests: 3, Assertions: 6, Failures: 3.
Уже лучше. Уже проходит в два раза больше проверок. Идём по порядку:
1. testStringCannotBeEmpty. Строка не может быть пустой. Добавляем проверку:
public function saveData() { if (!strlen($this->str)) return false; ...... }
2. testIntMustBeGreaterThanTenAdnSmallerThanTwenty. Условие 10<x<20. Проверка:
public function saveData() { if (!strlen($this->str)) return false; if ($this->num<10 || $this->num>20) return false; ...... }
3. testSaveLoad. Ага! ещё одна ошибка, на первый взгляд её сложно заметить. Строка записанная не равна строке прочитанной. Всё дело в конце строки. Идём в документацию и читаем или узнаём про флаг FILE_IGNORE_NEW_LINES.
Исправляем.
public function loadData() { $arr=file($this->fname, FILE_IGNORE_NEW_LINES); .... }
(spoiler: условие 2 специально не соблюдено)
Запустим:
There was 1 failure: 1) TestModelTest::testIntMustBeGreaterThanTenAdnSmallerThanTwenty Failed asserting that true is false. TestModelTest.php:30 C:\Program Files\php\phpunit:46 FAILURES! Tests: 3, Assertions: 8, Failures: 1.
Смотрим на 46 строчку (у меня): $model->setAttributes(20,'test3'); Мы не рассмотрели крайний случай! Исправляем:
public function saveData() { if (!strlen($this->str)) return false; if ($this->num<=10 || $this->num>=20) return false; ...... }
Запускаем наши тесты:
Time: 0 seconds, Memory: 2.75Mb OK (3 tests, 11 assertions)
Ура, все три теста прошли. Наша модель удовлетворяет поставленным требованиям. Что и требовалось :)
Заключение
Эта статья ни в коей мере не претендует на полное руководство по unit-тестированию, ни тем более на руководст��о по TDD. Цель данной статьи — в первую очередь систематизировать мои(начинающего) познания в данной сфере. И я очень надеюсь, что она поможет кому-нибудь в качестве начального подспорья для погружения в глубокий мир автоматического тестирования.
Спасибо за внимание.
