2009-05-22 3 views
28

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

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

ответ

34

Вы можете модульно протестировать процедурный PHP, без проблем. И вам определенно не повезло, если ваш код смешался с HTML. ($_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); 
} 

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

На нижних уровнях существуют незначительные вариации в зависимости от области видимости переменной и работают ли функции по побочным эффектам (возвращаются 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); 
} 

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

/* 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); 
    } 

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

+0

Это не очень хорошо, если код, который вы тестируете, усеян выражениями 'exit'' :( –

+3

@YarekT Никакое тестирование, возможно, не стоит хорошо, если код усеян выражениями' exit' или 'die'. Самый проверяемый способ обработать ожидаемое завершение сценария, путем исключения исключения и регистрации специального обработчика исключений, который достаточно умен, чтобы игнорировать настраиваемый тип исключения при обнаружении. Затем вы можете проверить, что выбрано ожидаемое исключение. Конечно, хорошо разработанное приложение действительно должно " когда-либо нужно «выйти» или «умереть» после фазы загрузки. – rdlowrey

1

Вы могли бы попытаться включить ваш код не ООП в тестовый класс с использованием

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'); 
} 
+0

Это похоже на интересное решение, однако, что, если большая часть вашей логики не включает в себя функции, а вместо этого простые циклы и условные обозначения? –

+5

Тогда у вас есть большая помощь от спагетти. –

+0

Я предположил, что у Travis нет кода php с html. –

6
тесты

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

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

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. Они позволят вам автоматизировать браузер, а затем проверить страницы для контента как конкретные местоположения/в формах.

+0

Я могу определенно разделить мой код на отдельные единицы. Ваш пример выглядит многообещающим, я полагаю, это не использует какую-либо конкретную структуру? –

+0

Да, это просто псевдо-тестовый код, но фактические тесты SImpleTest и тесты PHPUnit выглядят очень похожими. –