Skip to content

Helpers

Michael Hernandez edited this page Nov 27, 2018 · 8 revisions

This is a tutorial for using https://github.com/etsy/phpunit-extensions/tree/master/PHPUnit/Extensions/Helper.

In a unit test there should be absolutely no control structures.

Unit tests are meant to minimize your debug time by being small to the point tests on small specific pieces of code.

Since unit tests are intended to reduce the time spent debugging, it should make sense that the unit tests themselves should not be subject to debugging.

All logic should be tested. Adding control structures to tests is logic that should to be tested, and things that should be tested are possibly things that you may end up having to debug.

If for some reason you really need logic to accomplish something in your test, it should be pulled out into a well tested helper so that you don't end up debugging your tests in addition to your code.

AccessibleObject

AccessibleObject is a wrapper for an object that contains private or protected methods that you would like to test.

In general you should never be in a position to do this, but many feel like they should do this terrible dance to test private methods in isolation:

  • See a private method you want to test in isolation
  • Upgrade the private method to protected
  • Extend the class in the TestCase code file
  • Override all protected (should be private) methods you would like to test in isolation to be public
  • Test the new child class instead of the actual class

Note: You will not be able to use AccessibleObject to call into private methods involving reference parameters. Until variadics and argument unpacking are unveiled in PHP 5.6, the most reasonable workarounds are (unfortunately) to make the method public within the class, or perhaps more attractively, to factor the method out into the public interface of a new class.

Production Code

In your production code, annotate any private and protected methods that you would like to make accessible for testing with @accessibleForTesting:

class ObjectWithPrivate {

    private function myInaccessiblePrivateMethod() {
        return 'inaccessible';
    }

    /** @accessibleForTesting */
    private function myAccessiblePrivateMethod() {
        return 'accessible';
    }
}

Test Code

In your test code, simply wrap your object with PHPUnit_Extensions_Helper_AccessibleObject:

// alias the namespace
use PHPUnit\Extensions\Helper\AccessibleObject;

class ObjectWithPrivateTest extends PHPUnit_Framework_TestCase {

    private $accessible;

    protected setUp() {
        parent::setUp();
        $this->accessible = new AccessibleObject(new ObjectWithPrivate());
    }
}

Since myInaccessiblePrivateMethod() is not annotated, it will throw a ReflectionException when called:

public function testInaccessible() {
    $this->expectException(ReflectionException::class);
    $this->accessible->myInaccessiblePrivateMethod();
}

Since myAccessiblePrivateMethod() is annotated, it will be callable as though it were an accessible method call on the original object:

public function testAccessible() {
    $this->assertEquals('accessible', $this->accessible->myAccessiblePrivateMethod());
}

Further Examples

The Tests

Clone this wiki locally