programing

PHP로 단위 테스트를 어떻게 작성합니까?

nasanasas 2020. 8. 27. 07:50
반응형

PHP로 단위 테스트를 어떻게 작성합니까? [닫은]


나는 그들이 얼마나 위대한 지에 대해 모든 곳에서 읽었지만 어떤 이유로 내가 정확히 어떻게 무언가를 테스트 해야하는지 알아낼 수없는 것 같습니다. 누군가가 예제 코드를 게시 할 수 있으며 어떻게 테스트 할 수 있습니까? 너무 문제가 아니라면 :)


배우기 훨씬 쉬운 세 번째 "프레임 워크"가 있습니다. Simple Test 보다 훨씬 쉽습니다 . 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설치 해야 하며 , 이는 대부분의 상황에서 매우 일반적입니다. 설치가 필요한 경우 사용 가능한 최신 버전을 설치하는 것이 좋습니다. 설정하는 데 도움이 필요한 경우 언제든지 문의하십시오 (OS 등 제공).


단위 테스트에 사용할 수있는 프레임 워크는 두 가지입니다. 내가 선호하는 SimpletestPHPUnit . PHPUnit의 홈페이지에서 테스트를 작성하고 실행하는 방법에 대한 튜토리얼을 읽어보세요. 아주 쉽고 잘 설명되어 있습니다.


이를 수용하도록 코딩 스타일을 변경하여 단위 테스트를보다 효과적으로 만들 수 있습니다.

Google Testing 블로그 , 특히 Writing Testable Code 에 대한 게시물을 탐색하는 것이 좋습니다 .


다른 사람의 일을하는 방법을 배울 시간이 없었기 때문에 내 자신을 굴려서 글을 쓰는 데 약 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();

결과는 다음과 같습니다.

테스트 : TestOne은 실패했습니다. 
/ **
*이 테스트는 실패하도록 설계되었습니다.
** / (줄 : 149-152; 파일 : /Users/kris/Desktop/Testable.php)
테스트 : TestTwo는 성공했습니다. 

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

간단한 테스트와 문서화의 경우, php-doctest 는 매우 훌륭하며 별도의 파일을 열 필요가 없기 때문에 시작하는 정말 쉬운 방법입니다. 아래 기능을 상상해보십시오.

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

If you now run this file through phpdt (command-line runner of php-doctest) 1 test will be run. The doctest is contained inside the < code > block. Doctest originated in python and is fine for giving useful & runnable examples on how the code is supposed to work. You can't use it exclusively because the code itself would litter up with test cases but I've found that it's useful alongside a more formal tdd library - i use phpunit.

This 1st answer here sums it up nicely (it's not unit vs doctest ).


phpunit is pretty much the defacto unit testing framework for php. there is also DocTest (available as a PEAR package) and a few others. php itself is tested for regressions and the like via phpt tests which can also be run via pear.


Codeception tests are much like common unit tests but are much powerful in things where you need mocking and stubbing.

Here is the sample controller test. Notice how easily stubs are created. How easily you check the method was invoked.

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

Also there are other cool things. You can test database state, filesystem, etc.


Besides the excellent suggestions about test frameworks already given, are you building your application with one of the PHP web frameworks that has automated testing built in, such as Symfony or CakePHP? Sometimes having a place to just drop in your test methods reduces the start-up friction some people associate with automated testing and TDD.


Way too much to re-post here, but here is a great article on using phpt. It covers a number of aspects around phpt that are often overlooked, so it could be worth a read to expand your knowledge of php beyond just writing a test. Fortunately the article also discusses writing tests!

The main points of discussion

  1. Discover how marginally documented aspects of PHP work (or pretty much any part for that matter)
  2. Write simple unit tests for your own PHP code
  3. Write tests as part of an extension or to convey a potential bug to the internals or QA groups

I know there is a lot of info here already, but since this still shows up on Google searches i might as well add Chinook Test Suite to the list. It is a simple and small test framework.

You can easily test your classes with it and also create mock objects. You run the tests through a web browser and (not yet) through a console. In the browser you can specify what test class or even what test method to run. Or you can simply run all tests.

A screenshot from the github page:

Chinook Unit Test framework

What i like about it is the way you assert tests. This is done with so called "fluent assertions". Example:

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

And creating mock objects is a breeze too (with a fluent like syntax):

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

Anyway, more info can be found on the github page with a code example as well:

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

참고URL : https://stackoverflow.com/questions/282150/how-do-i-write-unit-tests-in-php

반응형