Web development and useful information

Nathan's Blog

Unit test private methods in PHP

Requires: PHP 5.3.2 or greater.
If you’re not running 5.3.2+ you can test private methods indirectly by creating a public method, that calls your private method.

Problem

We have a class, StatFinder which contains a private method, getDomainFromEmail() that we want to test.

<?php
class StatFinder
{
    // Get domain from email address
    private function getDomainFromEmail($email='')
    {
        if ($email == '') return '';
        $parts = explode('@', $email);
        if ($parts === false || !isset($parts[1])) return '';

        return $parts[1];
    }
}

We create a test to check that calling getEmailFromDomain(‘iliketurtles@gmail.com’) returns the expected ‘gmail.com‘. I’m using PHPUnit to run the unit test.

<?php
include('../libraries/StatFinder.php');

class StatFinderTest extends PHPUnit_Framework_TestCase
{
    public function testGetDomainFromEmail()
    {
        $sf = new StatFinder();
        $email = 'iliketurtles@gmail.com';
        $expected = 'gmail.com';
        $this->assertEquals($expected, $sf->getDomainFromEmail($email));
    }
}

When run, the test fails because the method is private, so only accessible by the class that defined it.


Failed unit test – (PHPUnit running in PhpStorm)

Solution

You can test private and protected methods by using the ReflectionMethod class, part of the Reflection API that comes with PHP 5.
The setAccessible method of the ReflectionMethod class is only available from PHP 5.3.2 up so you’ll have to be running at least this to use this.

<?php
include('../libraries/StatFinder.php');

class StatFinderTest extends PHPUnit_Framework_TestCase
{
    public function testGetDomainFromEmail()
    {
        $method = new ReflectionMethod('StatFinder', 'getDomainFromEmail');
        $method->setAccessible(true);

        $email = 'iliketurtles@gmail.com';
        $expected = 'gmail.com';
        $this->assertEquals($expected, $method->invoke(new StatFinder, $email));
    }
}

Explanation

<?php
$method = new ReflectionMethod('StatFinder', 'getDomainFromEmail');

This creates an instance of the ReflectionMethod class.
The first argument, StatFinder is the class that contains the private method to test.
The second argument, getDomainFromEmail is the method to test.

<?php
$method->setAccessible(true);

setAccessible(true) allows the private method, getDomainFromEmail to be called from inside the test.

<?php
$this->assertEquals($expected, $method->invoke(new StatFinder, $email));

invoke() invokes the private method.
The first argument, new StatFinder is the object to invoke the method on. For static methods, pass null.
The second argument, $email is the argument to pass to the method. Zero or more arguments can be passed to the method this way.

We run our updated test and it now works.


Successful test

Related

Previous

Uses for pegs

Next

Vim: delete lines that don’t match pattern

5 Comments

  1. Williams

    Thank you for sharing the information. I am currently doing my final year project and I have chosen PHP for doing the project. Using this private method will be more helpful to me while proceeding with the project. Keep updating.
    Scam OmniTech Support

  2. Great post. Recently i implement phpunit on symfony2 usring phpstorm and i enjoy it. I think that TDD should be the future of software develop.

  3. Johnny

    Thank you, it’s perfect !

  4. Max

    Good article.

  5. Fox

    Anyone who is using PHP 5.4 and up, there is actually an alternative method to that of reflection:

    https://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Powered by WordPress & Theme by Anders Norén