| |
| — | wiki:devel:parser:test:simple_test [2025/02/01 09:53] (текущий) – создано - внешнее изменение 127.0.0.1 |
|---|
| | ======Simple Test for PHP====== |
| | Новости: Начался цикл релиза 1.0.1. Функции включают загрузку включаемых файлов, улучшенную поддержку PHP5 для фиктивных объектов и поддержку HTML-меток. Также проведена большая очистка имен методов и некоторый внутренний рефакторинг. |
| |
| | Далее предполагается, что вы знакомы с концепцией модульного тестирования, а также с языком веб-разработки PHP. Это руководство для нетерпеливого нового пользователя SimpleTest . Для более полной документации, особенно если вы новичок в модульном тестировании, см. текущую документацию , а для примеров тестовых случаев см. учебник по модульному тестированию . |
| | |
| | |
| | =====Быстрое использование тестера===== |
| | Среди инструментов тестирования программного обеспечения модульный тестер является наиболее близким к разработчику. В контексте гибкой разработки тестовый код находится прямо рядом с исходным кодом, поскольку оба пишутся одновременно. В этом контексте SimpleTest стремится стать полным решением для тестирования PHP-разработчиков и называется «Simple», потому что он должен быть простым в использовании и расширении. На самом деле это был не очень удачный выбор названия. Он включает в себя все типичные функции, которые можно ожидать от JUnit и портов PHPUnit , но также добавляет фиктивные объекты . Он также имеет некоторую функциональность JWebUnit . Сюда входит навигация по веб-страницам, тестирование cookie-файлов и отправка форм. |
| | |
| | Самый быстрый способ продемонстрировать это — привести пример. |
| | |
| | Предположим, мы тестируем простой класс регистрации файла, который называется Log in classes/log.php . Начнем с создания тестового скрипта, который назовем tests/log_test.php и заполним его следующим образом... |
| | |
| | <code php> |
| | <?php |
| | require_once('simpletest/unit_tester.php'); |
| | require_once('simpletest/reporter.php'); |
| | require_once('../classes/log.php'); |
| | |
| | class TestOfLogging extends UnitTestCase { |
| | } |
| | ?> |
| | </code> |
| | |
| | Здесь папка //simpletest// либо локальная, либо находится в пути. Вам придется отредактировать эти расположения в зависимости от того, где вы разместили набор инструментов. <fc #22b14c>TestOfLogging</fc> — наш первый тестовый случай, и в настоящее время он пуст. |
| | Теперь у нас есть пять строк кода scaffolding и все еще нет тестов. Однако с этой части мы получаем возврат наших инвестиций очень быстро. Предположим, что класс <fc #22b14c>Log</fc> принимает имя файла для записи в конструкторе, и у нас есть временная папка, в которую можно поместить этот файл... |
| | |
| | <code php> |
| | <?php |
| | require_once('simpletest/unit_tester.php'); |
| | require_once('simpletest/reporter.php'); |
| | require_once('../classes/log.php'); |
| | |
| | class TestOfLogging extends UnitTestCase { |
| | |
| | function testCreatingNewFile() { |
| | @unlink('/temp/test.log'); |
| | $log = new Log('/temp/test.log'); |
| | $this->assertFalse(file_exists('/temp/test.log')); |
| | $log->message('Should write this to a file'); |
| | $this->assertTrue(file_exists('/temp/test.log')); |
| | } |
| | } |
| | ?> |
| | </code> |
| | |
| | При запуске тестового случая он ищет любой метод, начинающийся со строки <fc #22b14c>test</fc>, и выполняет этот метод. Обычно у нас, конечно, больше одного тестового метода. Утверждения в тестовых методах запускают сообщения в тестовую среду, которая немедленно отображает результат. Этот немедленный ответ важен не только в случае, если код вызывает сбой, но и для того, чтобы операторы <fc #22b14c>print</fc> могли отображать свое содержимое прямо рядом с соответствующим тестовым случаем. |
| | |
| | Чтобы увидеть эти результаты, нам нужно запустить тесты. Если это единственный тестовый случай, который мы хотим запустить, мы можем добиться этого с помощью... |
| | |
| | <code php> |
| | <?php |
| | require_once('simpletest/unit_tester.php'); |
| | require_once('simpletest/reporter.php'); |
| | require_once('../classes/log.php'); |
| | |
| | class TestOfLogging extends UnitTestCase { |
| | |
| | function testCreatingNewFile() { |
| | @unlink('/temp/test.log'); |
| | $log = new Log('/temp/test.log'); |
| | $this->assertFalse(file_exists('/temp/test.log')); |
| | $log->message('Should write this to a file'); |
| | $this->assertTrue(file_exists('/temp/test.log')); |
| | } |
| | } |
| | |
| | $test = &new TestOfLogging(); |
| | $test->run(new HtmlReporter()); |
| | ?> |
| | </code> |
| | |
| | В случае сбоя дисплей выглядит так... |
| | |
| | |
| | <note> |
| | **testoflogging**\\ |
| | Fail: testcreatingnewfile->True assertion failed.\\ |
| | <fc white/red>1/1 test cases complete. 1 passes and 1 fails.</fc> |
| | </note> |
| | |
| | ...и если это пройдет вот так... |
| | |
| | <note> |
| | **testoflogging**\\ |
| | <fc white/green>1/1 test cases complete. 2 passes and 0 fails.</fc> |
| | </note> |
| | |
| | И если вы это получите... |
| | |
| | <code> |
| | Fatal error: Failed opening required '../classes/log.php' (include_path='') in |
| | /home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php on line 7 |
| | </code> |
| | |
| | это означает, что у вас отсутствует файл //classes/Log.php//, который может выглядеть так... |
| | |
| | <code php> |
| | <?php |
| | class Log { |
| | |
| | function Log($file_path) { |
| | } |
| | |
| | function message() { |
| | } |
| | } |
| | ?>; |
| | </code> |
| | |
| | =====Групповые тесты===== |
| | |
| | Маловероятно, что в реальном приложении мы когда-либо запустим только один тестовый случай. Это означает, что нам нужен способ группировки случаев в тестовый сценарий, который может, при необходимости, запустить каждый тест в приложении. |
| | |
| | Наш первый шаг — удалить включения и отменить наш предыдущий взлом... |
| | |
| | <code php> |
| | <?php |
| | require_once('../classes/log.php'); |
| | |
| | class TestOfLogging extends UnitTestCase { |
| | |
| | function testCreatingNewFile() { |
| | @unlink('/temp/test.log'); |
| | $log = new Log('/temp/test.log'); |
| | $this->assertFalse(file_exists('/temp/test.log')); |
| | $log->message('Should write this to a file'); |
| | $this->assertTrue(file_exists('/temp/test.log')); |
| | } |
| | } |
| | ?> |
| | </code> |
| | |
| | |
| | Далее мы создаем новый файл с именем ''tests/all_tests.php'' и вставляем следующий код... |
| | |
| | <code php> |
| | <?php |
| | require_once('simpletest/unit_tester.php'); |
| | require_once('simpletest/reporter.php'); |
| | |
| | $test = &new GroupTest('All tests'); |
| | $test->addTestFile('log_test.php'); |
| | $test->run(new HtmlReporter()); |
| | ?> |
| | </code> |
| | |
| | Метод <fc #22b14c>GroupTest::addTestFile()</fc> включит файл тестового случая и прочитает все новые созданные классы, которые являются потомками <fc #22b14c>SimpleTestCase</fc>, одним из примеров которых является <fc #22b14c>UnitTestCase</fc>. На данный момент сохраняются только имена классов, чтобы тестовый исполнитель мог создать экземпляр класса, когда он будет проходить через ваш тестовый набор. |
| | Чтобы это работало правильно, файл тестового случая не должен слепо включать какие-либо другие расширения тестового случая, которые на самом деле не запускают тесты. Это может привести к подсчету дополнительных тестовых случаев во время тестового прогона. Вряд ли это серьезная проблема, но чтобы избежать этого неудобства, просто добавьте директиву <fc #22b14c>SimpleTestOptions::ignore()</fc> где-нибудь в файле тестового случая. Кроме того, файл тестового случая не должен был быть включен в другом месте, иначе в этот групповой тест не будут добавлены случаи. Это будет более серьезной ошибкой, так как если классы тестового случая уже загружены PHP, метод <fc #22b14c>GroupTest::addTestFile()</fc> не обнаружит их. |
| | |
| | Для отображения результатов необходимо только вызвать //tests/all_tests.php// с веб-сервера. |
| | |
| | =====Использование фиктивных объектов===== |
| | Давайте перенесемся еще дальше в будущее. |
| | |
| | Предположим, что наш класс логирования протестирован и завершен. Предположим также, что мы тестируем другой класс, который требуется для записи сообщений журнала, скажем, <fc #22b14c>SessionPool</fc>. Мы хотим протестировать метод, который, вероятно, в конечном итоге будет выглядеть так... |
| | |
| | <code php> |
| | class SessionPool { |
| | ... |
| | function logIn($username) { |
| | ... |
| | $this->_log->message("User $username logged in."); |
| | ... |
| | } |
| | ... |
| | } |
| | </code> |
| | |
| | В духе повторного использования мы используем наш класс <fc #22b14c>Log</fc>. Обычный тестовый случай может выглядеть так... |
| | |
| | <code php> |
| | <?php |
| | require_once('../classes/log.php'); |
| | require_once('../classes/session_pool.php'); |
| | |
| | class TestOfSessionLogging extends UnitTestCase { |
| | |
| | function setUp() { |
| | @unlink('/temp/test.log'); |
| | } |
| | |
| | function tearDown() { |
| | @unlink('/temp/test.log'); |
| | } |
| | |
| | function testLogInIsLogged() { |
| | $log = new Log('/temp/test.log'); |
| | $session_pool = &new SessionPool($log); |
| | $session_pool->logIn('fred'); |
| | $messages = file('/temp/test.log'); |
| | $this->assertEqual($messages[0], "User fred logged in.\n"); |
| | } |
| | } |
| | ?> |
| | </code> |
| | |
| | Этот дизайн тестового случая не совсем плох, но его можно улучшить. Мы тратим время на возню с файлами журналов, которые не являются частью нашего теста. Хуже того, мы создали тесные связи между классом Log и этим тестом. Что, если мы больше не будем использовать файлы, а вместо этого будем использовать библиотеку syslog ? Вы заметили дополнительный возврат каретки в сообщении? Это было добавлено регистратором? Что, если он также добавил временную метку или другие данные? |
| | Единственная часть, которую мы действительно хотим проверить, — это то, что определенное сообщение было отправлено в регистратор. Мы уменьшаем связанность, если можем передать фальшивый класс регистрации, который просто записывает вызовы сообщения для тестирования, но не предпринимает никаких действий. Однако он должен выглядеть точно так же, как наш оригинал. |
| | |
| | Если поддельный объект не пишет в файл, то мы экономим на удалении файла до и после каждого теста. Мы могли бы сэкономить еще больше тестового кода, если бы поддельный объект любезно выполнил утверждение для нас. |
| | |
| | |
| | Слишком хорошо, чтобы быть правдой? К счастью, мы можем легко создать такой объект... |
| | |
| | <code php> |
| | <?php |
| | require_once('../classes/log.php'); |
| | require_once('../classes/session_pool.php'); |
| | Mock::generate('Log'); |
| | |
| | class TestOfSessionLogging extends UnitTestCase { |
| | |
| | function testLogInIsLogged() { |
| | $log = &new MockLog(); |
| | $log->expectOnce('message', array('User fred logged in.')); |
| | $session_pool = &new SessionPool($log); |
| | $session_pool->logIn('fred'); |
| | } |
| | } |
| | ?> |
| | </code> |
| | |
| | Тест будет запущен, когда вызов <fc #22b14c>message()</fc> будет вызван на объекте <fc #22b14c>MockLog</fc>. Вызов mock запустит сравнение параметров, а затем отправит результирующее событие pass или fail на дисплей теста. Здесь также можно включить подстановочные знаки, чтобы тесты не стали слишком конкретными. |
| | Если мок достигает конца тестового случая без вызова метода, ожидание <fc #22b14c>expectOnce()</fc> вызовет сбой теста. Другими словами, мок может обнаружить как отсутствие поведения, так и его наличие. |
| | |
| | Для фиктивных объектов в наборе SimpleTest можно задать произвольные возвращаемые значения, последовательности возвращаемых значений, возвращаемые значения, выбранные в соответствии с входящими аргументами, последовательности ожидаемых параметров и ограничения на количество вызовов метода. |
| | |
| | Для запуска этого теста библиотека фиктивных объектов должна быть включена в тестовый набор, например, в //all_tests.php//. |
| | |
| | |
| | =====Тестирование веб-страниц===== |
| | |
| | Одним из требований к веб-сайтам является то, что они производят веб-страницы. Если вы создаете проект сверху вниз и хотите полностью интегрировать тестирование по ходу дела, то вам понадобится способ автоматической навигации по сайту и проверки вывода на корректность. Это работа веб-тестера. |
| | |
| | Веб-тестирование в SimpleTest довольно примитивно, например, нет JavaScript. Чтобы дать представление, вот тривиальный пример, где извлекается домашняя страница, с которой мы переходим на страницу «about», а затем тестируем некоторый определенный клиентом контент. |
| | |
| | <code php> |
| | <?php |
| | require_once('simpletest/web_tester.php'); |
| | require_once('simpletest/reporter.php'); |
| | |
| | class TestOfAbout extends WebTestCase { |
| | |
| | function setUp() { |
| | $this->get('http://test-server/index.php'); |
| | $this->click('About'); |
| | } |
| | |
| | function testSearchEngineOptimisations() { |
| | $this->assertTitle('A long title about us for search engines'); |
| | $this->assertPattern('/a popular keyphrase/i'); |
| | } |
| | } |
| | $test = &new TestOfAbout(); |
| | $test->run(new HtmlReporter()); |
| | ?> |
| | </code> |
| | |
| | Используя этот код в качестве приемочного теста, вы можете быть уверены, что контент всегда соответствует спецификациям как разработчиков, так и других заинтересованных сторон проекта. |
| | |
| | =====Дополнения и Файлы===== |
| | * [[http://web.archive.org/web/20070814114708/http://www.lastcraft.com/simple_test.php#unit|Ссылка на оригинальную статью]] |
| | * [[https://simpletest.sourceforge.net/en/start-testing.html|simpletest.sourceforge.net]] |
| | * [[https://github.com/simpletest/simpletest|github_simpletest]] |
| | * {{ :wiki:devel:parser:test:simpletest-main.zip |SimpleTest}} |