RikkeiSoft Co. Ltd.,

How to make unit test with PHPUnit

Created by Oanh Nguyen

Nội dung

  1. Cài đặt PHPUnit
  2. Tổ chức test suite
  3. Viết test class cơ bản

Yêu cầu

user@host~$ php -v
PHP 5.6.19 (cli) (built: Mar  3 2016 06:29:24)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
with Xdebug v2.3.3, Copyright (c) 2002-2015, by Derick Rethans

user@host~$ php -m | grep 'dom\|json\|pcre\|Xdebug\|tokenizer\|xmlwriter'
dom
json
pcre
tokenizer
xmlwriter
Xdebug

Download PHP Archive (PHAR)

Unix OS

user@host~$ chmod +x phpunit.phar
user@host~$ sudo mv phpunit.phar /usr/local/bin/phpunit
user@host~$ phpunit --version

Windows OS

C:\Downloads> mkdir C:\bin
C:\Downloads> copy phpunit.phar C:\bin
C:\Downloads> cd C:\bin
C:\bin> echo @php "%~dp0phpunit.phar" %* > phpunit.cmd
C:\bin> set PATH=%PATH%;C:\bin
C:\bin> phpunit --version

Using composer (recommend)

Cần cài Composer trước

Local installation

user@host~$ composer require --dev "phpunit/phpunit=5.2.*"
user@host~$ ./vendor/bin/phpunit --version

Global installation

user@host~$ composer global require "phpunit/phpunit=5.2.*"
user@host~$ phpunit --version

Chạy PHPUnit

Organizing Tests

PHPUnit conventions

  • Tên class với tên file test phải giống nhau
  • Tên test class phải kết thúc bằng từ Test và thuờng giống tên class
  • Test class phải kế thừa từ class PHPUnit_Framework_TestCase
  • Test methods phải bắt đầu bằng test và là public methods

Project example

project
├── composer.json
├── composer.lock
├── phpunit.xml.dist
├── src
│   └── Calculator.php
├── tests
│   ├── bootstrap.php
│   └── CalculatorTest.php
└── vendor

Begin write first test

<?php
namespace Demo\Calculator\Tests;

class CalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testAdd()
{
    $this->assertSame(10, Calculator::add(5, 5));
}
}

Using assert* methods

  • assertNull, assertEmpty, assertNan, assertInstanceOf, ...
  • assertEquals, assertGreaterThan, assertGreaterThanOrEqual, assertSame, ...
  • assertStringMatchesFormat, assertStringStartsWith, assertStringEndsWith, assertRegExp, ...
  • assertArrayHasKey, assertArraySubset, assertContains, assertCount, ...
  • assertInstanceOf, assertObjectHasAttribute, assertClassHasAttribute, ...

Test dependencies

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
public function testEmpty()
{
    $stack = array();
    $this->assertEmpty($stack);

    return $stack;
}

/**
 * @depends testEmpty
 */
public function testPush(array $stack)
{
    array_push($stack, 'foo');
    $this->assertEquals('foo', $stack[count($stack)-1]);
    $this->assertNotEmpty($stack);

    return $stack;
}

/**
 * @depends testPush
 */
public function testPop(array $stack)
{
    $this->assertEquals('foo', array_pop($stack));
    $this->assertEmpty($stack);
}
}

Data providers

/**
* @dataProvider providerMethodName
*/

providerMethodName là public method

Not using data providers

<?php
namespace Demo\Calculator\Tests;

class CalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testAdd()
{
    $this->assertSame(10, Calculator::add(5, 5));
    $this->assertSame(10, Calculator::add(-5, 15));
    $this->assertSame(10, Calculator::add(0, 10));
}
}

Using data providers

<??php
namespace Demo\Calculator\Tests;

class CalculatorTest extends \PHPUnit_Framework_TestCase
{
/**
 * @dataProvider dataForTestAdd
 */
public function testAdd($a, $b, $expected)
{
    $this->assertSame($expected, Calculator::add($a, $b));
}

public function dataForTestAdd
{
    return [
        //[$a, $b, $expected],
        [5, 5, 10],
        [-5, 15, 10],
        [0, 10, 10],
    ];
}
}

Testing Exception

<?php
namespace Demo\Calculator\Tests;

class CalculatorTest extends PHPUnit_Framework_TestCase
{
public function testDivByZero()
{
    $this->expectException(\InvalidArgumentException::class); // exception class name
    $this->expectExceptionMessage('Division by zero');        // exception mesage
    Calculator::div(10, 0);
}
}

Testing Exception with annotations

<?php
namespace Demo\Calculator\Tests;

class CalculatorTest extends PHPUnit_Framework_TestCase
{
/**
 * Test case dividing by zero
 *
 * @expectedException \InvalidArgumentException
 * @expectedExceptionMessage Division by zero
 */
public function testDivByZero()
{
    Calculator::div(10, 0);
}
}

Testing PHP errors, warnings and notices

PHPUnit chuyển các PHP errors, warnings và notices xuất hiện trong quá trình thực thi test thành các exception

  • PHPUnit_Framework_Error
  • PHPUnit_Framework_Warning
  • PHPUnit_Framework_Notice

Testing PHP errors

<?php
namespace Demo\Calculator\Tests;

class CalculatorTest extends PHPUnit_Framework_TestCase
{
public function testDivByZero()
{
    $this->expectException(\PHPUnit_Framework_Error::class); // exception class name
    $this->expectExceptionMessage('Division by zero');        // exception mesage
    $a = 10 / 0;
}
}

Testing PHP warnings & notices

Tương tự với PHP error nhưng sử dụng PHPUnit_Framework_Error_NoticePHPUnit_Framework_Error_Warning.

Test PHP output

Làm sao để test được method có print hay echo?

Sử dụng 2 method expectOutputString()expectOutputRegex()

Test PHP output

<?php
class OutputTest extends PHPUnit_Framework_TestCase
{
public function testExpectFooActualFoo()
{
    $this->expectOutputString('foo');
    print 'foo';
}

public function testExpectBarActualBaz()
{
    $this->expectOutputRegex('/^baz(\d*)$/');
    print 'baz' . rand(1, 1000);
}
}

Tổng kết

  1. Cài đặt PHPUnit
  2. Tổ chức test suite
  3. Viết test class cơ bản

Q & A

Documents may be found in http://oanhnn.github.io

Example may be found in Github

THE END

- PHPUnit site
- Wikipedia about PHPUnit
- Tutorial about PHPUnit of Juan Tremini