Stubbing and Mocking Static Methods with PHPUnit

PHPUnit has ability to stub and mock static methods.

Consider the class Foo:

<?php
class Foo
{
    public static function doSomething()
    {
        return static::helper();
    }
 
    public static function helper()
    {
        return 'foo';
    }
}
?>

To test the static helper() function with PHPUnit, you can write you test like that:
<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    public function testDoSomething()
    {
        $class = $this->getMockClass(
          'Foo',          /* name of class to mock     */
          array('helper') /* list of methods to mock   */
        );
 
        $class::staticExpects($this->any())
              ->method('helper')
              ->will($this->returnValue('bar'));
 
        $this->assertEquals(
          'bar',
          $class::doSomething()
        );
    }
}
?>
The new staticExpects() method works similar to the non-static expects() variant. This approach only works for the stubbing and mocking of static method calls where caller and callee are in the same class. This is because static methods are death to testability:
"Unit-Testing needs seams, seams is where we prevent the execution of normal code path and is how we achieve isolation of the class under test. Seams work through polymorphism, we override/implement class/interface and then wire the class under test differently in order to take control of the execution flow. With static methods there is nothing to override. Yes, static methods are easy to call, but if the static method calls another static method there is no way to override the called method dependency."
Update: Above feature has been dropped from PHPUnit 3.7 https://github.com/sebastianbergmann/phpunit-documentation/issues/77

Few best practices

One Assertion per Test

Few tips and best practices on how to write your unit tests efficiently. One of the best advises would be "one test = one assertion". You may think that cutting corners may be a good idea; well, think again. Here's a little test scenario - you need to test if the function returns string. You name this function as "testIfFunctionReturnsString". And while asserting string, you may be tempted to also do other assertions - why not test for that as well?

Consider following test:
<?php

public function testIfFunctionReturnsString(){
  $string = "Hello World";
  $this->assertGreaterThan(0,strlen($string));
  $this->assertContains("99",$string);
}

?>

In this case you are testing string length and also if string contains "99". Second assertion will fail, yet the result can be deceptive and might cause confusion what caused the error.

Be descriptive about what you are testing

<?php
class Money extends PHPUnit_Framework_TestCase
{
    public function testCanBeNegated()
    {
        $a = new Money();
        $b = $a->Negate(1);
        $this->assertEquals(-1, $b->getAmount());
    }
}
?>

vendor/bin/phpunit --stderr --testdox tests

Running this will print nice, human readable output which is easy to understand
PHPUnit 3.7.24 by Sebastian Bergmann.

Money
 [x] Can be negated

Conclusion

Start testing your PHP applications today if you haven't already.

PHPUnit

Comments

Popular posts from this blog

MongoDB: Remove an Arbiter From a Replica Set

Enable HTTP/2 Support in AWS ELB