Unit Testing with PHPUnit

by Joeri Timmermans

Who am i?

  • 27 years old
  • Gamer, PHP Developer
    with a passion for Food & Drinks
  • Interactive Multimedia Design
  •      @Joeri_timmer
  •      /pix-art
  •      www.pix-art.be
  • What is Unit Testing?
  • Debunking the myths
  • Why do Unit Testing?
  • PHPUnit
  • Starting with PHPUnit
  • Basic assertions
  • Hello world!
  • Oooops!
  • Fixtures
  • Mock objects
  • Database testing
  • Interesting reads
  • Questions?

//TODO:

What is Unit Testing?

 

Wikipedia:

"Unit testing is a software testing method by which individual units of source code are tested to determine if they are fit for use"

Debunking the myths

  • It Takes Too Long
  • There’s No Need to Test:
    My Code Already Works!
  • Expensive
  • It’s No Fun
  • Automation
  • Readable code == Testable code
  • Test code on functionality
  • Reduce bugs in new and existing features
  • Reduce cost of changes
  • Easier refactoring
  • Progress indication of the project
  • Alerts generation for CI-tools
  • Forces you to slow down and think
  • Speed up development and reduce fear

Why do (Unit) Testing

PHPUnit

  • Created by Sebastian Bergmann
  • Part of xUnit Family
  •      /sebastianbergmann/phpunit

Starting with PHPUnit

  • Download phar:
    https://phpunit.de
     
  • Composer: 
    "phpunit/phpunit": "4.3.*"
     
  • Setup phpunit.xml

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="bootstrap/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"
>
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory>./app/tests/</directory>
        </testsuite>
    </testsuites>
</phpunit>

Info: https://phpunit.de/manual/current/en/appendixes.configuration.html

Basic assertions

  • AssertTrue/AssertFalse - Check the input to verify it equals true/false

  • AssertEquals - Check the result against another input for a match

  • AssertGreaterThan - Check the result to see if it’s larger than a value
    (there’s also LessThan, GreaterThanOrEqual, and LessThanOrEqual)

  • AssertContains - Check that the input contains a certain value

  • AssertType - Check that a variable is of a certain type

  • AssertNull - Check that a variable is null

  • AssertFileExists - Verify that a file exists

  • AssertRegExp - Check the input against a regular expression

  • ...

Hello world!

<?php 

namespace Example;

class HelloWorld
{
	public $helloWorld;

	public function __construct($string = 'Hello World!')
	{
		$this->helloWorld = $string;
	}

	public function sayHello()
	{
		return $this->helloWorld;
	}
}

Our first test

<?php

namespace Example;

class HelloWorldTest extends \PHPUnit_Framework_TestCase
{

    public function testSayHello()
    {        
        $hw = new HelloWorld();
        $string = $hw->sayHello();
        $this->assertEquals('Hello World!', $string);
    }

}

The result!

PHPUnit 4.2.5 by Sebastian Bergmann.

Configuration read from /var/www/example/build/phpunit.xml

.

Time: 70 ms, Memory: 3.50Mb

OK (1 test, 1 assertion)

Oooops!

<?php

namespace Example;

class OopsTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @expectedException InvalidArgumentException
     * @expectedExceptionMessage Wrong argument!
     */
    public function testOops()
    {        
        throw new \InvalidArgumentException("Wrong argument!");
    }

}

The result!

PHPUnit 4.2.5 by Sebastian Bergmann.

Configuration read from /var/www/example/build/phpunit.xml

.

Time: 60 ms, Memory: 3.25Mb

OK (1 test, 2 assertions)

Fixtures

  • "Known state" of an application
    • needs to be "set up" at start
    • needs to be "torn down" at end
    • shares "states" over test methods
<?php

namespace Example;

class MyListTest extends \PHPUnit_Framework_TestCase
{

    protected $myList;

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

    public function testMyListEmpty()
    {
        $this->assertEquals(0, sizeof($this->myList));
    }

    public function testMyListHasOne()
    {
        array_push($this->myList, 'myItem');
        $this->assertEquals(1, sizeof($this->myList));
    }

    public function tearDown()
    {
        unset($this->myList);
    }
    
}

The result!

PHPUnit 4.2.5 by Sebastian Bergmann.

Configuration read from /var/www/example/build/phpunit.xml

..

Time: 67 ms, Memory: 3.50Mb

OK (2 tests, 2 assertions)

Mock objects

  • Simulated objects
  • Mimics API or behaviour
  • In a controlled way
  • To test real objects

Mock objects

<?php 

namespace Example;

class HelloMailer
{
	public $mailer;

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

	public function sendMessage($message)
	{
		return $this->mailer->send($message);
	}
}
<?php

namespace Example;

class HelloMailerTest extends \PHPUnit_Framework_TestCase
{
    protected $mailer;

    public function setUp()
    {
        $mailer = $this->getMockBuilder('Example\Mailer')
                           ->setMockClassName('Mailer')
                           ->disableOriginalConstructor()
                           ->setMethods(array('send'))
                           ->getMock();

        $mailer->expects($this->any())
                   ->method('send')
                   ->with($this->equalTo('Hello mailer!'))
                   ->will($this->returnValue(true));

        $this->mailer = $mailer;
    }

    public function testSend()
    {   
        $hm = new HelloMailer($this->mailer);

        $response = $hm->sendMessage('Hello mailer!');
        $this->assertTrue($response);
    }

}

The result!

PHPUnit 4.2.5 by Sebastian Bergmann.

Configuration read from /var/www/example/build/phpunit.xml

.

Time: 80 ms, Memory: 4.00Mb

OK (1 test, 1 assertion)

Database testing

  • DbUnit supports: MySQL, PostgreSQL, Oracle and SQLite
  • ​getConnection()
  • getDataSet()
  • The four stages of a database test

    • Set up fixture

    • Exercise System Under Test

    • Verify outcome

    • Teardown

Behat

Feature: homepage
  This is a standard behat test to see if the homepage
  shows the right notifications

  Scenario: When i visit the homepage i should see notifications                                          # app/tests/features/homepage.feature:4
    Given I am on "/notifications"                                                                        # FeatureContext::visit()
    Then I should see "Docent A. Bastiaan zal afwezig zijn op 22/10/2014."                                # FeatureContext::assertPageContainsText()
    Then I should see "Op 6/10 zal er een persconferentie van Telenet 
plaatsvinden in de Creativity Gym."

1 scenario (1 gelukt)
3 stappen (3 gelukt)
0m0.11s
Scenario: When i visit the homepage i should see notifications
    Given I am on "/notifications"
    Then I should see "Docent A. Bastiaan zal afwezig zijn op 22/10/2014."
    Then I should see "Op 6/10 zal er een persconferentie van Telenet
    plaatsvinden in de Creativity Gym."

Interesting reads

  • https://phpunit.de/
  • http://www.dragonbe.com/
  • http://www.sitepoint.com/tutorial-introduction-to-unit-testing-in-php-with-phpunit/

Questions?

Thank you

  •      @Joeri_timmer
  •      /pix-art
  •      www.pix-art.be

Unit Testing with PHPUnit

By Joeri Timmermans

Unit Testing with PHPUnit

Introduction to Unit Testing with PHPUnit

  • 878