Подтвердить что ты не робот

Как написать модульные тесты в PHP?

Я читал всюду о том, насколько они хороши, но по какой-то причине я не могу понять, как именно я должен что-то тестировать. Может ли кто-нибудь, возможно, разместить кусок примерного кода и как они его проведут? Если это не слишком много проблем:)

4b9b3361

Ответ 1

Существует 3-я "фреймворк", которая намного проще изучать - даже проще, чем SimpleTest, она называется phpt.

Праймер можно найти здесь: http://qa.php.net/write-test.php

Изменить: Просто посмотрел ваш запрос на пример кода.

Предположим, что у вас есть следующая функция в файле lib.php:

<?php
function foo($bar)
{
  return $bar;
}
?>

Действительно простой и прямой, возвращаемый параметр. Поэтому давайте посмотрим на тест для этой функции, мы будем называть тестовый файл foo.phpt:

--TEST--
foo() function - A basic test to see if it works. :)
--FILE--
<?php
include 'lib.php'; // might need to adjust path if not in the same dir
$bar = 'Hello World';
var_dump(foo($bar));
?>
--EXPECT--
string(11) "Hello World"

В двух словах мы предоставляем параметр $bar значением "Hello World", а мы var_dump() ответ вызова функции на foo().

Для запуска этого теста используйте: pear run-test path/to/foo.phpt

Для этого требуется рабочая установка PEAR в вашей системе, что довольно часто встречается в большинстве случаев. Если вам необходимо установить его, я рекомендую установить последнюю версию. Если вам нужна помощь в настройке, не стесняйтесь спрашивать (но предоставить ОС и т.д.).

Ответ 2

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

Ответ 4

Я катался самостоятельно, потому что у меня не было времени, чтобы научиться чему-то elses делать что-то, это заняло около 20 минут, чтобы написать, 10, чтобы адаптировать его для публикации здесь.

Для меня полезно использовать очень.

это довольно длинный, но он сам объясняет, и внизу есть пример.

/**
 * Provides Assertions
 **/
class Assert
{
    public static function AreEqual( $a, $b )
    {
        if ( $a != $b )
        {
            throw new Exception( 'Subjects are not equal.' );
        }
    }
}

/**
 * Provides a loggable entity with information on a test and how it executed
 **/
class TestResult
{
    protected $_testableInstance = null;

    protected $_isSuccess = false;
    public function getSuccess()
    {
        return $this->_isSuccess;
    }

    protected $_output = '';
    public function getOutput()
    {
        return $_output;
    }
    public function setOutput( $value )
    {
        $_output = $value;
    }

    protected $_test = null;
    public function getTest()
    {
        return $this->_test;
    }

    public function getName()
    {
        return $this->_test->getName();
    }
    public function getComment()
    {
        return $this->ParseComment( $this->_test->getDocComment() );
    }

    private function ParseComment( $comment )
    {
        $lines = explode( "\n", $comment );
        for( $i = 0; $i < count( $lines ); $i ++ )
        {
            $lines[$i] = trim( $lines[ $i ] );
        }
        return implode( "\n", $lines );
    }

    protected $_exception = null;
    public function getException()
    {
        return $this->_exception;
    }

    static public function CreateFailure( Testable $object, ReflectionMethod $test, Exception $exception )
    {
        $result = new self();
        $result->_isSuccess = false;
        $result->testableInstance = $object;
        $result->_test = $test;
        $result->_exception = $exception;

        return $result;
    }
    static public function CreateSuccess( Testable $object, ReflectionMethod $test )
    {
        $result = new self();
        $result->_isSuccess = true;
        $result->testableInstance = $object;
        $result->_test = $test;

        return $result;
    }
}

/**
 * Provides a base class to derive tests from
 **/
abstract class Testable
{
    protected $test_log = array();

    /**
     * Logs the result of a test. keeps track of results for later inspection, Overridable to log elsewhere.
     **/
    protected function Log( TestResult $result )
    {
        $this->test_log[] = $result;

        printf( "Test: %s was a %s %s\n"
            ,$result->getName()
            ,$result->getSuccess() ? 'success' : 'failure'
            ,$result->getSuccess() ? '' : sprintf( "\n%s (lines:%d-%d; file:%s)"
                ,$result->getComment()
                ,$result->getTest()->getStartLine()
                ,$result->getTest()->getEndLine()
                ,$result->getTest()->getFileName()
                )
            );

    }
    final public function RunTests()
    {
        $class = new ReflectionClass( $this );
        foreach( $class->GetMethods() as $method )
        {
            $methodname = $method->getName();
            if ( strlen( $methodname ) > 4 && substr( $methodname, 0, 4 ) == 'Test' )
            {
                ob_start();
                try
                {
                    $this->$methodname();
                    $result = TestResult::CreateSuccess( $this, $method );
                }
                catch( Exception $ex )
                {
                    $result = TestResult::CreateFailure( $this, $method, $ex );
                }
                $output = ob_get_clean();
                $result->setOutput( $output );
                $this->Log( $result );
            }
        }
    }
}

/**
 * a simple Test suite with two tests
 **/
class MyTest extends Testable
{
    /**
     * This test is designed to fail
     **/
    public function TestOne()
    {
        Assert::AreEqual( 1, 2 );
    }

    /**
     * This test is designed to succeed
     **/
    public function TestTwo()
    {
        Assert::AreEqual( 1, 1 );
    }
}

// this is how to use it.
$test = new MyTest();
$test->RunTests();

Выводится:

Test: TestOne was a failure 
/**
* This test is designed to fail
**/ (lines:149-152; file:/Users/kris/Desktop/Testable.php)
Test: TestTwo was a success 

Ответ 5

Получить PHPUnit. Он очень прост в использовании.

Тогда начните с очень простых утверждений. Вы можете сделать много с AssertEquals, прежде чем попасть в что-нибудь еще. Это хороший способ намочить ноги.

Вы также можете попробовать сначала написать свой тест (так как вы задали свой вопрос TDD-тегом), а затем написать свой код. Если вы этого не сделали, прежде чем это станет открытием.

require_once 'ClassYouWantToTest';
require_once 'PHPUnit...blah,blah,whatever';

class ClassYouWantToTest extends PHPUnit...blah,blah,whatever
{
    private $ClassYouWantToTest;

   protected function setUp ()
    {
        parent::setUp();
        $this->ClassYouWantToTest = new ClassYouWantToTest(/* parameters */);
    }

    protected function tearDown ()
    {
        $this->ClassYouWantToTest = null;
        parent::tearDown();
    }

    public function __construct ()
    {   
        // not really needed
    }

    /**
     * Tests ClassYouWantToTest->methodFoo()
     */
    public function testMethodFoo ()
    {
        $this->assertEquals(
            $this->ClassYouWantToTest->methodFoo('putValueOfParamHere), 'expectedOutputHere);

    /**
     * Tests ClassYouWantToTest->methodBar()
     */
    public function testMethodFoo ()
    {
        $this->assertEquals(
            $this->ClassYouWantToTest->methodBar('putValueOfParamHere), 'expectedOutputHere);
}

Ответ 6

Для простых тестов и документации php-doctest довольно хорошо, и это очень простой способ начать работу, так как у вас нет для открытия отдельного файла. Представьте себе следующую функцию:

/**
* Sums 2 numbers
* <code>
* //doctest: add
* echo add(5,2);
* //expects:
* 7
* </code>
*/
function add($a,$b){
    return $a + $b;   
}

Если вы запустите этот файл через phpdt (runter php-doctest) из командной строки, будет запущен тест. Учение содержится внутри <code> . Doctest возникла в python и отлично подходит для предоставления полезных и управляемых примеров того, как должен работать код. Вы не можете использовать его исключительно потому, что сам код будет мутить тестовые примеры, но я обнаружил, что он полезен наряду с более формальной библиотекой tdd - я использую phpunit.

Этот 1-й ответ здесь прекрасно подбирает его (это не единица против доктрины).

Ответ 7

phpunit - это в значительной степени инфраструктурный модуль тестирования defacto для php. есть также DocTest (доступно как пакет PEAR) и несколько других. php сам тестируется на регрессии и т.д. посредством phpt tests, который также может быть запущен через грушу.

Ответ 8

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

Вот тест контроллера образца. Обратите внимание, как легко создаются заглушки. Как легко вы проверили метод, был вызван.

<?php
use Codeception\Util\Stub as Stub;

const VALID_USER_ID = 1;
const INVALID_USER_ID = 0;

class UserControllerCest {
public $class = 'UserController';


public function show(CodeGuy $I) {
    // prepare environment
    $I->haveFakeClass($controller = Stub::makeEmptyExcept($this->class, 'show'));
    $I->haveFakeClass($db = Stub::make('DbConnector', array('find' => function($id) { return $id == VALID_USER_ID ? new User() : null ))); };
    $I->setProperty($controller, 'db', $db);

    $I->executeTestedMethodOn($controller, VALID_USER_ID)
        ->seeResultEquals(true)
        ->seeMethodInvoked($controller, 'render');

    $I->expect('it will render 404 page for non existent user')
        ->executeTestedMethodOn($controller, INVALID_USER_ID)
        ->seeResultNotEquals(true)
        ->seeMethodInvoked($controller, 'render404','User not found')
        ->seeMethodNotInvoked($controller, 'render');
}
}

Также есть и другие интересные вещи. Вы можете проверить состояние базы данных, файловую систему и т.д.

Ответ 9

Помимо отличных предложений по уже существующим тестовым фреймворкам, вы создаете свое приложение с помощью одной из веб-фреймворков PHP с автоматическим тестированием, например Symfony или CakePHP? Иногда наличие места для простота в методах тестирования снижает начальное трение, которое некоторые люди связывают с автоматическим тестированием и TDD.

Ответ 10

Слишком много для повторной публикации здесь, но вот отличная статья об использовании phpt. Он охватывает несколько аспектов вокруг phpt, которые часто упускаются из виду, поэтому было бы полезно прочитать, чтобы расширить свои знания php за пределами простого написания теста. К счастью, в статье также обсуждаются письменные тесты!

Основные моменты обсуждения

  • Узнайте, как незначительно документированные аспекты работы PHP (или почти любая часть в этом отношении)
  • Напишите простые модульные тесты для собственного PHP-кода.
  • Записывать тесты как часть расширения или передавать потенциальную ошибку внутренним или группам качества.

Ответ 11

Я знаю, что здесь уже много информации, но так как это все еще появляется в результатах поиска Google, я могу добавить в список Chinook Test Suite. Это простая и небольшая тестовая структура.

Вы можете легко протестировать свои классы, а также создать макет объектов. Вы запускаете тесты через веб-браузер и (еще не) через консоль. В браузере вы можете указать, какой тест-класс или какой метод тестирования выполнить. Или вы можете просто запустить все тесты.

Снимок экрана с страницы github:

Chinook Unit Test framework

Что мне нравится, так это то, как вы утверждаете тесты. Это делается с помощью так называемых "плавных утверждений". Пример:

$this->Assert($datetime)->Should()->BeAfter($someDatetime);

И создание макетных объектов тоже ветерок (с плавным синтаксисом):

$mock = new CFMock::Create(new DummyClass());
$mock->ACallTo('SomeMethod')->Returns('some value');

В любом случае, дополнительную информацию можно найти на странице github с примером кода:

https://github.com/w00/Chinook-TestSuite