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


97

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


5
균형을 위해,이 없다, 또는 PHP를위한 프레임 워크를 테스트 3 단위 - 여기 저기 목록입니다 : en.wikipedia.org/wiki/List_of_unit_testing_frameworks#PHP
펜톤

답변:


36

배우기 훨씬 쉬운 세 번째 "프레임 워크"가 있습니다. 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 등 제공).


그럴까요 run-tests?
Dharman

30

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


21

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

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


7
훌륭한 게시물을 언급 한 것 같습니다. '단위 테스트는 그다지 효과적이지 않습니다'로 답변을 시작하면 거의 테스트에 능숙 해지면서 반대표를 던졌습니다.
xtofl 2013

2
@xtofl은 '긍정 성'을 약간 높이기 위해 편집했습니다. :)
icc97

13

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

7

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

5

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

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

이제 phpdt (php-doctest의 명령 줄 실행기)를 통해이 파일을 실행하면 1 개의 테스트가 실행됩니다. doctest는 <code> 블록 안에 포함되어 있습니다. Doctest는 파이썬에서 시작되었으며 코드 작동 방식에 대한 유용하고 실행 가능한 예제를 제공하는 데 좋습니다. 코드 자체가 테스트 케이스로 가득 차 있기 때문에 독점적으로 사용할 수는 없지만 좀 더 공식적인 tdd 라이브러리와 함께 유용하다는 것을 발견했습니다. 저는 phpunit을 사용합니다.

이 첫 번째 대답 멋지게 요약합니다 (unit vs doctest가 아닙니다).


1
소스가 약간 어수선하지 않습니까?
Ali Ghanavatian 2015

할 수 있습니다. 단일 단순 테스트에만 사용해야합니다. 또한 문서로도 사용됩니다. 더 많은 사용 단위 테스트가 필요한 경우.
Sofia

2

phpunit은 php를위한 사실상의 단위 테스트 프레임 워크입니다. 또한이 doctest가 (배 패키지로 제공) 및 몇 가지 다른. php 자체는 pear를 통해 실행할 수있는 phpt 테스트 를 통해 회귀 등을 테스트합니다 .


2

코드 셉션 테스트는 일반적인 단위 테스트와 매우 비슷하지만 조롱과 스터 빙이 필요한 경우 훨씬 강력합니다.

다음은 샘플 컨트롤러 테스트입니다. 스텁이 얼마나 쉽게 작성되는지 확인하십시오. 메소드가 호출되었는지 얼마나 쉽게 확인할 수 있습니다.

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

또한 다른 멋진 것들이 있습니다. 데이터베이스 상태, 파일 시스템 등을 테스트 할 수 있습니다.


1

이미 제공된 테스트 프레임 워크에 대한 훌륭한 제안 외에도 Symfony 또는 CakePHP 와 같이 자동화 된 테스트가 내장 된 PHP 웹 프레임 워크 중 하나로 애플리케이션을 구축하고 있습니까? 때로는 테스트 방법을 중단 할 수있는 장소가 있으면 일부 사람들이 자동화 된 테스트 및 TDD와 관련된 시작 마찰을 줄일 수 있습니다.


1

여기에 다시 게시하기에는 너무 많지만 여기 에 phpt 사용에 대한 훌륭한 기사 가 있습니다. 그것은 종종 간과되는 phpt 와 관련된 여러 측면을 다루기 때문에 단순히 테스트를 작성하는 것 이상으로 PHP에 대한 지식을 확장하기 위해 읽을 가치가 있습니다. 다행히이 기사는 쓰기 테스트에 대해서도 설명합니다!

논의의 요점

  1. PHP의 미미하게 문서화 된 측면 (또는 그 문제에 대한 거의 모든 부분)이 어떻게 작동하는지 알아보십시오.
  2. 자신의 PHP 코드에 대한 간단한 단위 테스트 작성
  3. 확장의 일부로 테스트를 작성하거나 내부 또는 QA 그룹에 잠재적 버그를 전달합니다.

1

이미 여기에 많은 정보가 있다는 것을 알고 있지만 여전히 Google 검색에 표시되므로 Chinook Test Suite 를 목록에 추가 하는 것이 좋습니다. 간단하고 작은 테스트 프레임 워크입니다.

이를 사용하여 클래스를 쉽게 테스트하고 모의 객체를 만들 수도 있습니다. 웹 브라우저를 통해 그리고 (아직은 아님) 콘솔을 통해 테스트를 실행합니다 . 브라우저에서 실행할 테스트 클래스 또는 테스트 방법을 지정할 수 있습니다. 또는 단순히 모든 테스트를 실행할 수 있습니다.

github 페이지의 스크린 샷 :

치누크 단위 테스트 프레임 워크

내가 좋아하는 것은 테스트를 주장하는 방식입니다. 이것은 소위 "유창한 주장"으로 이루어집니다. 예:

$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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.