수색…


통사론

  • 단언의 완전한 목록 . 예 :
  • assertTrue(bool $condition[, string $messageIfFalse = '']);
  • assertEquals(mixed $expected, mixed $actual[, string $messageIfNotEqual = '']);

비고

Unit 테스트는 소스 코드를 테스트하여 예상되는 입력을 처리하는지 여부를 확인하는 데 사용됩니다. Unit 테스트는 대부분의 프레임 워크에서 지원됩니다. PHPUnit 테스트 에는 여러 가지가 있으며 구문이 다를 수 있습니다. 이 예제에서는 PHPUnit 사용하고 있습니다.

클래스 규칙 테스트

규칙 () 메소드가있는 간단한 LoginForm 클래스 (프레임 워크 템플릿으로 로그인 페이지에서 사용됨)가 있다고 가정 해 보겠습니다.

class LoginForm {
    public $email;
    public $rememberMe;
    public $password;

    /* rules() method returns an array with what each field has as a requirement.
     * Login form uses email and password to authenticate user.
     */
    public function rules() {
        return [
            // Email and Password are both required
            [['email', 'password'], 'required'],

            // Email must be in email format
            ['email', 'email'],

            // rememberMe must be a boolean value
            ['rememberMe', 'boolean'],

            // Password must match this pattern (must contain only letters and numbers)
            ['password', 'match', 'pattern' => '/^[a-z0-9]+$/i'],
        ];
    }

    /** the validate function checks for correctness of the passed rules */
    public function validate($rule) {
        $success = true;
        list($var, $type) = $rule;
        foreach ((array) $var as $var) {
            switch ($type) {
                case "required":
                    $success = $success && $this->$var != "";
                    break;
                case "email":
                    $success = $success && filter_var($this->$var, FILTER_VALIDATE_EMAIL);
                    break;
                case "boolean":
                    $success = $success && filter_var($this->$var, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null;
                    break;
                case "match":
                    $success = $success && preg_match($rule["pattern"], $this->$var);
                    break;
                default:
                    throw new \InvalidArgumentException("Invalid filter type passed")
            }
        }
        return $success;
    }
}

이 클래스에 대한 테스트를 수행하기 위해 Unit 테스트 (소스 코드를 확인하여 기대에 부합하는지 확인)를 사용합니다.

class LoginFormTest extends TestCase {
    protected $loginForm;

    // Executing code on the start of the test
    public function setUp() {
        $this->loginForm = new LoginForm;
    }

    // To validate our rules, we should use the validate() method

    /**
     * This method belongs to Unit test class LoginFormTest and
     * it's testing rules that are described above.
     */
    public function testRuleValidation() {
        $rules = $this->loginForm->rules();

        // Initialize to valid and test this
        $this->loginForm->email = "[email protected]";
        $this->loginForm->password = "password";
        $this->loginForm->rememberMe = true;
        $this->assertTrue($this->loginForm->validate($rules), "Should be valid as nothing is invalid");

        // Test email validation
        // Since we made email to be in email format, it cannot be empty
        $this->loginForm->email = '';
        $this->assertFalse($this->loginForm->validate($rules), "Email should not be valid (empty)");

        // It does not contain "@" in string so it's invalid
        $this->loginForm->email = 'invalid.email.com';
        $this->assertFalse($this->loginForm->validate($rules), "Email should not be valid (invalid format)");

        // Revert email to valid for next test
        $this->loginForm->email = '[email protected]';

        // Test password validation
        // Password cannot be empty (since it's required)
        $this->loginForm->password = '';
        $this->assertFalse($this->loginForm->validate($rules), "Password should not be valid (empty)");

        // Revert password to valid for next test
        $this->loginForm->password = 'ThisIsMyPassword';

        // Test rememberMe validation
        $this->loginForm->rememberMe = 999;
        $this->assertFalse($this->loginForm->validate($rules), "RememberMe should not be valid (integer type)");

        // Revert remeberMe to valid for next test
        $this->loginForm->rememberMe = true;
    }
}

Unit 테스트가 여기에 도움이 될 수있는 방법은 무엇입니까 (일반적인 예제 제외)? 예를 들어, 예상치 못한 결과가 나올 때 아주 잘 맞습니다. 예를 들어,이 규칙을 앞에서 취해 보겠습니다.

['password', 'match', 'pattern' => '/^[a-z0-9]+$/i'],

대신, 우리가 한 가지 중요한 것을 놓치고 이것을 썼다면 :

['password', 'match', 'pattern' => '/^[a-z0-9]$/i'],

수십 개의 다른 규칙 (전자 메일과 암호 만 사용하는 것이 아니라고 가정)을 사용하면 실수를 감지하기가 어렵습니다. 이 단위 테스트 :

// Initialize to valid and test this
$this->loginForm->email = "[email protected]";
$this->loginForm->password = "password";
$this->loginForm->rememberMe = true;
$this->assertTrue($this->loginForm->validate($rules), "Should be valid as nothing is invalid");

첫 번째 예는 통과하지만 번째 예는 통과하지 않습니다. 왜? 두 번째 예에서는 오타 (누락 된 + 기호)가있는 패턴을 작성 했으므로 하나의 문자 / 숫자 만 허용합니다.

단위 테스트는 phpunit [path_to_file] 명령으로 콘솔에서 실행할 수 있습니다. 모든 것이 정상이면 모든 테스트가 OK 상태인지 확인해야합니다. 그렇지 않으면 Error (구문 오류) 또는 Fail (해당 메서드에서 적어도 한 줄은 통과하지 못했음)가 표시됩니다.

--coverage 와 같은 추가 매개 변수를 사용하여 백엔드 코드의 몇 줄이 테스트되었고 통과 / 실패했는지 시각적으로 볼 수 있습니다. 이것은 PHPUnit 을 설치 한 모든 프레임 워크에 적용됩니다.

PHPUnit 테스트가 콘솔에서 어떻게 보이는지 예제 (이 예제에 따르지 않는 일반적인 모양) :

여기에 이미지 설명을 입력하십시오.

PHPUnit 데이터 제공 업체

테스트 방법에서는 종종 테스트 할 데이터가 필요합니다. 일부 메소드를 완전히 테스트하려면 가능한 모든 테스트 조건에 대해 서로 다른 데이터 세트를 제공해야합니다. 물론 다음과 같이 루프를 사용하여 수동으로 할 수 있습니다.

...
public function testSomething()
{
    $data = [...];
    foreach($data as $dataSet) {
       $this->assertSomething($dataSet);
    }
}
... 

누군가가 편리하다는 것을 알 수 있습니다. 그러나이 접근법에는 몇 가지 단점이 있습니다. 먼저, 테스트 함수가 여러 매개 변수를 허용하는 경우 데이터를 추출하기 위해 추가 작업을 수행해야합니다. 둘째, 실패시 추가 메시지 및 디버깅없이 실패한 데이터 세트를 구별하기가 어려울 수 있습니다. 셋째, PHPUnit은 데이터 공급자를 사용하여 테스트 데이터 세트를 자동으로 처리합니다.

데이터 공급자는 특정 테스트 케이스에 대한 데이터를 반환해야하는 함수입니다.

데이터 공급자 메서드는 public이어야하며 Iterator 인터페이스를 구현하는 배열 또는 개체 의 배열을 반환하고 각 반복 단계마다 배열생성해야합니다 . 컬렉션의 일부인 각 배열에 대해 배열의 내용을 인수로 사용하여 테스트 메서드가 호출됩니다.

테스트에 데이터 공급자를 사용하려면 지정된 데이터 공급자 함수 이름과 함께 @dataProvider 주석을 사용합니다.

/**
* @dataProvider dataProviderForTest
*/
public function testEquals($a, $b)
{
    $this->assertEquals($a, $b);
}

public function dataProviderForTest()
{
    return [
        [1,1],
        [2,2],
        [3,2] //this will fail
    ];
}

배열의 배열

dataProviderForTest() 는 배열의 배열을 반환합니다. 중첩 된 각 배열에는 두 개의 요소가 있으며 testEquals() 필요한 매개 변수를 testEquals() 채 웁니다. 이렇게 오류가 발생합니다. 충분한 요소가없는 경우 Missing argument 2 for Test::testEquals() 됩니다. PHPUnit은 자동으로 데이터를 순환하고 테스트를 실행합니다 :

public function dataProviderForTest()
{
    return [
        [1,1], // [0] testEquals($a = 1, $b = 1)
        [2,2], // [1] testEquals($a = 2, $b = 2)
        [3,2]  // [2] There was 1 failure: 1) Test::testEquals with data set #2 (3, 4)
    ];
}

편의를 위해 각 데이터 세트의 이름 을 지정할 수 있습니다. 실패한 데이터를 쉽게 감지 할 수 있습니다.

public function dataProviderForTest()
{
    return [
        'Test 1' => [1,1], // [0] testEquals($a = 1, $b = 1)
        'Test 2' => [2,2], // [1] testEquals($a = 2, $b = 2)
        'Test 3' => [3,2]  // [2] There was 1 failure: 
                           //     1) Test::testEquals with data set "Test 3" (3, 4)
    ];
}

반복기

class MyIterator implements Iterator {
    protected $array = [];

    public function __construct($array) {
        $this->array = $array;
    }

    function rewind() {
        return reset($this->array);
    }

    function current() {
        return current($this->array);
    }

    function key() {
        return key($this->array);
    }

    function next() {
        return next($this->array);
    }

    function valid() {
        return key($this->array) !== null;
    }
}
...

class Test extends TestCase
{
    /**
     * @dataProvider dataProviderForTest
     */
    public function testEquals($a)
    {
        $toCompare = 0;

        $this->assertEquals($a, $toCompare);
    }

    public function dataProviderForTest()
    {
        return new MyIterator([
            'Test 1' => [0],
            'Test 2' => [false],
            'Test 3' => [null]
        ]);
    }
}

보시다시피 단순한 반복자도 작동합니다.

단일 매개 변수의 경우에도 데이터 공급자는 배열 [$parameter] 반환해야합니다 [$parameter]

왜냐하면 우리는 current() 메소드 (모든 반복에서 실제로 데이터를 리턴)를 this로 변경하면 :

function current() {
    return current($this->array)[0];
}

또는 실제 데이터를 변경하십시오.

return new MyIterator([
            'Test 1' => 0,
            'Test 2' => false,
            'Test 3' => null
        ]);

오류가 발생합니다.

There was 1 warning:

1) Warning
The data provider specified for Test::testEquals is invalid.

물론, 간단한 배열을 통해 Iterator 객체를 사용하는 것은 유용하지 않습니다. 그것은 당신의 사건에 대한 몇 가지 구체적인 논리를 구현해야합니다.

발전기

그것은 명시 적으로 언급되지 않고 매뉴얼에 표시되어 있지만 데이터 제공자로 발전기 를 사용할 수도 있습니다. Generator 클래스는 실제로 Iterator 인터페이스를 구현합니다.

다음은 generator 와 결합 된 DirectoryIterator 의 사용 예입니다.

/**
 * @param string $file
 *
 * @dataProvider fileDataProvider
 */
public function testSomethingWithFiles($fileName)
{
    //$fileName is available here
    
    //do test here
}

public function fileDataProvider()
{
    $directory = new DirectoryIterator('path-to-the-directory');

    foreach ($directory as $file) {
        if ($file->isFile() && $file->isReadable()) {
            yield [$file->getPathname()]; // invoke generator here.
        }
    }
}

주 공급자 yield 배열이야. 대신 잘못된 데이터 공급자 경고가 표시됩니다.

예외 테스트

예외를 throw하는 메서드를 테스트하려고한다고 가정 해 봅시다.

class Car
{
    /**
     * @throws \Exception
     */
    public function drive()
    {
        throw new \Exception('Useful message', 1);
    }
}

메서드 호출을 try / catch 블록으로 묶고 실행 객체의 속성에 대한 어설 션을 작성하면이 작업을 수행 할 수 있지만보다 편리하게 예외 어설 션 메서드를 사용할 수 있습니다. PHPUnit 5.2에서 expectX () 메소드를 사용하여 예외 유형, 메시지 및 코드를 선언 할 수 있습니다.

class DriveTest extends PHPUnit_Framework_TestCase
{
    public function testDrive()
    {
        // prepare
        $car = new \Car();
        $expectedClass = \Exception::class;
        $expectedMessage = 'Useful message';
        $expectedCode = 1;

        // test
        $this->expectException($expectedClass);
        $this->expectMessage($expectedMessage);
        $this->expectCode($expectedCode);

        // invoke
        $car->drive();
    }
}

이전 버전의 PHPUnit을 사용하는 경우 expectX () 메소드 대신 setExpectedException 메소드를 사용할 수 있지만 더 이상 사용되지 않으며 버전 6에서 제거됩니다.

class DriveTest extends PHPUnit_Framework_TestCase
{
    public function testDrive()
    {
        // prepare
        $car = new \Car();
        $expectedClass = \Exception::class;
        $expectedMessage = 'Useful message';
        $expectedCode = 1;

        // test
        $this->setExpectedException($expectedClass, $expectedMessage, $expectedCode);

        // invoke
        $car->drive();
    }
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow