Как написать модульные тесты на PHP с процедурной базой кода?

Я в основном убежден в преимуществах модульного тестирования, и я хотел бы начать применять концепцию к большой существующей кодовой базе, написанной на PHP. Менее 10% этого кода является объектно-ориентированным.

Я рассмотрел несколько модулей тестирования модулей (PHPUnit, SimpleTest и phpt). Тем не менее, я не нашел примеров для любого из них, которые проверяют процедурный код. Какая лучшая структура для моей ситуации и есть ли примеры модульного тестирования PHP с использованием кода, отличного от OOP?

Вы можете тестировать процедурный PHP без проблем. И вам определенно не повезло, если ваш код смешался с HTML.

На уровне применения или приемочного теста ваш процедурный PHP, вероятно, зависит от значения суперглобалов ( $_POST, $_GET, $_COOKIE и т. Д.) Для определения поведения и заканчивается включением файла шаблона и выплевыванием вывода.

Чтобы выполнить тестирование на уровне приложения, вы можете просто установить суперглобальные значения; запустите выходной буфер (чтобы сохранить кучу html от наводнения вашего экрана); вызвать страницу; утверждать против вещей внутри буфера; и уничтожить буфер в конце. Итак, вы можете сделать что-то вроде этого:

 public function setUp() { if (isset($_POST['foo'])) { unset($_POST['foo']); } } public function testSomeKindOfAcceptanceTest() { $_POST['foo'] = 'bar'; ob_start(); include('fileToTest.php'); $output = ob_get_flush(); $this->assertContains($someExpectedString, $output); } с public function setUp() { if (isset($_POST['foo'])) { unset($_POST['foo']); } } public function testSomeKindOfAcceptanceTest() { $_POST['foo'] = 'bar'; ob_start(); include('fileToTest.php'); $output = ob_get_flush(); $this->assertContains($someExpectedString, $output); } в public function setUp() { if (isset($_POST['foo'])) { unset($_POST['foo']); } } public function testSomeKindOfAcceptanceTest() { $_POST['foo'] = 'bar'; ob_start(); include('fileToTest.php'); $output = ob_get_flush(); $this->assertContains($someExpectedString, $output); } 

Даже для огромных «фреймворков» с большим количеством включений такой тип тестирования скажет вам, есть ли у вас функции уровня приложения, работающие или нет. Это будет очень важно, когда вы начнете совершенствовать свой код, потому что даже если вы уверены, что соединитель базы данных все еще работает и выглядит лучше, чем раньше, вам нужно нажать кнопку и увидеть, что да, вы все равно можете войти и выйти из базы данных.

На более низких уровнях существуют незначительные вариации в зависимости от области видимости переменной, и работают ли функции по побочным эффектам (возврат true или false) или прямое возвращение результата.

Переменные передаются явно, как параметры или массивы параметров между функциями? Или переменные заданы во многих разных местах и ​​передаются неявно как глобальные? Если это (хороший) явный случай, вы можете выполнить модульную проверку функции с помощью (1), включая файл, содержащий функцию, затем (2) напрямую подать значения теста функции и (3) захватить вывод и утвердить его. Если вы используете глобальные переменные, вам просто нужно быть очень осторожным (как указано выше, в примере $ _POST), чтобы тщательно исключить все глобальные переменные между тестами. Также особенно полезно держать тесты очень маленькими (5-10 строк, 1-2 утверждения) при работе с функцией, которая толкает и тянет много глобалов.

Еще одна основная проблема заключается в том, работают ли функции, возвращая результат или изменяя переданные параметры, возвращая вместо этого true / false. В первом случае тестирование проще, но опять же, это возможно в обоих случаях:

 // assuming you required the file of interest at the top of the test file public function testShouldConcatenateTwoStringsAndReturnResult() { $stringOne = 'foo'; $stringTwo = 'bar'; $expectedOutput = 'foobar'; $output = myCustomCatFunction($stringOne, $stringTwo); $this->assertEquals($expectedOutput, $output); } 

В плохом случае, когда ваш код работает по побочным эффектам и возвращает true или false, вы все равно можете довольно легко протестировать:

 /* suppose your cat function stupidly * overwrites the first parameter * with the result of concatenation, * as an admittedly contrived example */ public function testShouldConcatenateTwoStringsAndReturnTrue() { $stringOne = 'foo'; $stringTwo = 'bar'; $expectedOutput = 'foobar'; $output = myCustomCatFunction($stringOne, $stringTwo); $this->assertTrue($output); $this->Equals($expectedOutput, $stringOne); } 

Надеюсь это поможет.

Какие модульные тесты преуспевают, и для чего их следует использовать, это когда у вас есть часть кода, в которой вы даете некоторое количество входов, и вы ожидаете получить некоторое количество выходов. Идея заключается в том, что когда вы добавите функциональность позже, вы можете запустить свои тесты и убедиться, что она по-прежнему выполняет старые функции одинаково.

Итак, если у вас есть процедурная база кода, вы можете выполнить это вызов своих функций в методах тестирования

 require 'my-libraries.php'; class SomeTest extends SomeBaseTestFromSomeFramework { public function testSetup() { $this->assertTrue(true); } public function testMyFunction() { $output = my_function('foo',3); $this->assertEquals('expected output',$output); } } 

Этот трюк с базой PHP-кода, часто ваш код библиотеки будет мешать работе вашей тестовой среды, так как ваша база кода и тестовые среды будут иметь много кода, связанных с настройкой среды приложения в веб-браузере (сеанс, общий доступ глобальные переменные и т. д.). Ожидайте потратить когда-нибудь, чтобы добраться до точки, где вы можете включить свой библиотечный код и запустить простой тест на загрязнение (функция testSetup выше).

Если ваш код не имеет функций, и это всего лишь серия файлов PHP, которые выводят HTML-страницы, вам не повезло. Ваш код не может быть разделен на отдельные единицы, что означает, что тестирование устройства не будет очень полезно для вас. Вам лучше провести время на уровне «приемочного тестирования» с такими продуктами, как Selenium и Watir . Они позволят вам автоматизировать браузер, а затем проверить страницы для контента как конкретные местоположения / в формах.

Вы можете попытаться включить ваш не-oop-код в тестовый класс, используя

 require_once 'your_non_oop_file.php' # Contains fct_to_test() 

И с помощью phpUnit вы определяете свою тестовую функцию:

 testfct_to_test() { assertEquals( result_expected, fct_to_test(), 'Fail with fct_to_test' ); }