Stubbing and Mocking Static Methods

Sebastian Bergmann » 12 February 2010 » in New Features » 4 Comments

This article is part of a series on testing untestable code:

With PHPUnit 3.5 it will be possible 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';
    }
}
?>

When testing Foo::doSomething() we want to decouple it from its dependency Foo::helper(). With PHPUnit 3.5 and PHP 5.3 as well as consistent use of late static binding (using static:: instead of self::) the following is possible:

<?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."
Defined tags for this entry: , ,

Testing Code That Uses Singletons

Sebastian Bergmann » 11 February 2010 » in Articles » 12 Comments

This article is part of a series on testing untestable code:

I frequently quote Miško Hevery with

"It is hard to test code that uses singletons."

And then my audience asks me ...

Why is it hard to test code that uses singletons?

Lets have a look at the default implementation of the Singleton design pattern in PHP:

<?php
class Singleton
{
    private static $uniqueInstance = NULL;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
}
?>

The code above declares a class that cannot be instantiated (or cloned) by a client using the new (or clone) operator(s). To get a reference to the only instance of the class one has to use the static method getInstance(). Usually the code that uses the Singleton (which we will refer to as client) is strongly coupled to the getInstance() method:

<?php
class Client
{
    public function doSomething()
    {
        $singleton = Singleton::getInstance();
 
        // ...
    }
}
?>

It is impossible to write a test for the doSomething() method without also invoking the singleton's getInstance() method. This means that we cannot get a fresh instance of the Singleton class and thus have no guarantee that there are no side effects in multiple tests that interact with the singleton.

Dependency Injection

Dependency Injection can help with decoupling the client from the getInstance() method:

<?php
class Client
{
    public function doSomething(Singleton $singleton = NULL)
    {
        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }
 
        // ...
    }
}
?>

Instead of unconditionally invoking the getInstance() method inside the doSomething() we can now optionally pass in an instance of the Singleton class. This allows us to pass in a test-specific equivalent such as a mock object or stub:

<?php
class ClientTest extends PHPUnit_Framework_TestCase
{
    public function testSingleton()
    {
        $singleton = $this->getMock(
          'Singleton', /* name of class to mock     */
          array(),     /* list of methods to mock   */
          array(),     /* constructor arguments     */
          '',          /* name for mocked class     */
          FALSE        /* do not invoke constructor */
        );
 
        // ... configure $singleton ...
 
        $client = new Client;
        $client->doSomething($singleton);
 
        // ...
    }
}
?>

Alternative Singleton Implementations

Either as an alternative or in addition to rewriting the clients to optionally accept an instance of the Singleton class as an argument, we can also rewrite the Singleton class to make testing easier.

Resettable Singleton

The first approach is to add a reset() method to the Singleton class:

<?php
class Singleton
{
    private static $uniqueInstance = NULL;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
 
    public static function reset() {
        self::$uniqueInstance = NULL;
    }
}
?>

Invoking the reset() method causes the getInstance() method to create a fresh object of the Singleton class the next time it is called.

Singleton with Test Context

The second approach is to add a testing context to the Singleton class:

<?php
class Singleton
{
    private static $uniqueInstance = NULL;
    public static $testing = FALSE;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL ||
            self::$testing) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
}
?>

Setting Singleton::$testing = TRUE; causes the getInstance() method to create a fresh object of the Singleton class each time it is called.

PHPUnit Can Help, Too

PHPUnit has a backup/restore mechanism for static attributes of classes.

This is yet another feature of PHPUnit that makes the testing of code that uses global state (which includes, but is not limited to, global and superglobal variables as well as static attributes of classes) easier.

Just Because You Can, Does Not Mean You Should

Yes, it is possible write testable code that uses singletons.
This does not mean, however, that you should use them without thinking twice.

Defined tags for this entry: , , ,

Testing Your Privates

Sebastian Bergmann » 09 February 2010 » in Articles » 13 Comments

This article is part of a series on testing untestable code:

No, not those privates. If you need help with those, this book might help.

One question I get over and over again when talking about Unit Testing is this:

"How do I test the private attributes and methods of my objects?"

Lets assume we have a class Foo:

<?php
class Foo
{
    private $bar = 'baz';
 
    public function doSomething()
    {
        return $this->bar = $this->doSomethingPrivate();
    }
 
    private function doSomethingPrivate()
    {
        return 'blah';
    }
}
?>

Before we explore how protected and private attributes and methods can be tested directly, lets have a look at how they can be tested indirectly.

The following test calls the testDoSomething() method which in turn calls the doSomethingPrivate() method:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers Foo::doSomething
     * @covers Foo::doSomethingPrivate
     */
    public function testDoSomething()
    {
        $foo = new Foo;
        $this->assertEquals('blah', $foo->doSomething());
    }
}
?>

The test above assumes that testDoSomething() only works correctly when testDoSomethingPrivate() works correctly. This means that we have indirectly tested testDoSomethingPrivate(). The problem with this approach is that when the test fails we do not know directly where the root cause for the failure is. It could be in either testDoSomething() or testDoSomethingPrivate(). This makes the test less valuable.

PHPUnit supports reading protected and private attributes through the PHPUnit_Framework_Assert::readAttribute() method. Convenience wrappers such as PHPUnit_Framework_TestCase::assertAttributeEquals() exist to express assertions on protected and private attributes:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    public function testPrivateAttribute()
    {
        $this->assertAttributeEquals(
          'baz',  /* expected value */
          'bar',  /* attribute name */
          new Foo /* object         */
        );
    }
}
?>

PHP 5.3.2 introduces the ReflectionMethod::setAccessible() method to allow the invocation of protected and private methods through the Reflection API:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers Foo::doSomethingPrivate
     */
    public function testPrivateMethod()
    {
        $method = new ReflectionMethod(
          'Foo', 'doSomethingPrivate'
        );
 
        $method->setAccessible(TRUE);
 
        $this->assertEquals(
          'blah', $method->invoke(new Foo)
        );
    }
}
?>

In the test above we directly test testDoSomethingPrivate(). When it fails we immediately know where to look for the root cause.

I agree with Dave Thomas and Andy Hunt, who write in their book "Pragmatic Unit Testing":

"In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out."

So: Just because the testing of protected and private attributes and methods is possible does not mean that this is a "good thing".

Defined tags for this entry: , ,

My Take on Facebook's HipHop for PHP

Sebastian Bergmann » 03 February 2010 » in Articles » 4 Comments

Disclosure: Since Facebook Inc. is a customer of thePHP.cc, I might be considered biased. For the purpose of this blog posting, consider me taking off my "thePHP.cc hat" and putting on my "PHP Community hat".

HipHop for PHP

The Announcement

Yesterday Facebook Inc. announced "HipHop for PHP", a source code transformer that turns PHP 5.2 code (minus some features) into C++ code that can then be compiled with g++, for instance, to a regular binary.

The Summit

If you are following some key PHP community members on Twitter, you may have learned that a couple of weeks ago Facebook invited a select few to Palo Alto, CA to give them a sneak peek at the technology.

Terry Chay has a great summary of this event (including photos):


At the Summit
Facebook, Palo Alto, California

This "private summit" is one of the reasons why this announcement was preceded with a lot of rumors. I am not really interested in rumors (or conspiracy theories, for that matter), so I will focus on the technology and its impact here.

My Opinion

I am very happy — both as a "programming language geek" as well as an Open Source enthusiast — that Facebook Inc. decided to open source their solution to their problem. Understanding this is crucial:

  • Facebook had a problem.
  • Facebook found a solution for their problem.
  • Facebook decided to share their solution with the PHP community at large.
    Kudos for that!

Now what was their problem?

Facebook has a large code base that would take a long time (one to two years by my estimation) to refactor or rewrite in either PHP or another programming language to achieve the performance improvement that Facebook was looking for. During this time of refactoring or rewriting, Facebook would not have been able to innovate.

In the past, Facebook tried to achieve performance improvements by contributing to PHP as well as APC. Many PHP deployments benefit from this contributions today, but for Facebook they were simply not enough.

From this perspective it makes a lot of sense to dramatically change the way that they deploy and execute their PHP code: without major modifications to their existing code base they are able to run it with a CPU usage reduced by 50%:

"Facebook sees about a 50% reduction in CPU usage when serving equal amounts of Web traffic when compared to Apache and PHP.

Facebook's API tier can serve twice the traffic using 30% less CPU."

This is a sustainable solution for them as it allows them to benefit from PHP's advantages such as shorter development cycles and lower training costs — while at the same time, thanks to HipHop, reducing their data center costs.

What others are saying ...

Terry Chay is spot on when he summarizes the impact of HipHop in his blog posting:

"Arguing against PHP for performance reasons no longer holds water. [...]

In practice, you can get the advantages of having a scripting language without operational costs. [...]

HipHop is a showcase. With it the PHP world can point to Facebook as being the busiest site built in a scripting language in the world."

Stefan Priebsch (also with thePHP.cc) makes the point that you need to rethink your PHP development before you can dance to the HipHop beat:

"HipHop executes PHP code bypassing the Zend Engine, so there is no guarantee that a program will behave exactly alike on PHP and HipHop. More and better automated unit and integration tests will be required to allow you to compare the results on PHP and on HipHop, and determine whether deviations are to be interpreted as a bug, or can be tolerated."

I could not agree more: As there is a real build — including a real compilation — with HipHop, best practices such as coding standards and continuous integration will be even more important.

In conclusion, I would like to welcome "HipHop for PHP" to the PHP ecosystem. Yes, it is not a solution for a problem faced by 99.9% of the PHP deployments out there. But this does not make it less interesting in any way.

One more thing ...

PHPUnit runs on "HipHop for PHP", details can be found in the wiki.

Defined tags for this entry: , , , ,

PHP Summit in Düsseldorf

Sebastian Bergmann » 26 January 2010 » in Events » 0 Comments

PHP Summit

This blog posting is in German as the event it relates to is German-only. Sorry for the inconvenience. Our next training event in English will be in Montréal in March.

Der 1. PHP SUMMIT ist eine neue und einzigartige Veranstaltung mit dem Anspruch, alle wichtigen Themen von PHP in kompakter Form zu vermitteln. Hier können Sie drei Tage lang, jenseits der ausgetretenen Pfade von klassischen IT-Trainings im Unterrichtsstil mit Übungsaufgaben und Lernkontrolle, insgesamt 18 intensive und interaktive Power Workshops erleben.

Sämtliche Workshops beziehen sich auf die tägliche Projektarbeit und zeigen Ihnen den produktiven Live- Einsatz von Tools und Methoden. Hohe Interaktion mit den Teilnehmern, Live-Coding statt vorgefertigter foo/bar Beispiele, Informationen über neueste Trends in der PHP-Entwicklung — alles gewürzt mit einer guten Prise Humor — das sind die einzigartigen Merkmale des PHP SUMMIT. Darüber hinaus profitieren Sie vom geballten Wissen und der Praxiserfahrung der drei Experten von thePHP.cc: Sebastian Bergmann, Arne Blankerts und Stefan Priebsch.

Melden Sie sich noch heute an, um sich Ihren Platz zu reservieren!

Defined tags for this entry: ,

PHP Days Montréal

Sebastian Bergmann » 21 January 2010 » in Events » 0 Comments

Together with my friends and partners from thePHP.cc, Arne Blankerts and Stefan Priebsch, I will be giving an "Advanced PHP Development" training in Montréal in March.

  • How much effort is required to adapt your code to your customers' change requests?
  • Is it easy, or is it cumbersome and tedious?
  • And what about your code's complexity and readability?
  • Is everything crystal clear, or are there parts of the code base that nobody dares to touch anymore?
  • How often do you need to fix bugs after a release?
  • And, by the way: how easy is it to roll a release anyway?

If any of these questions made you blush with embarrassment, it is high time that you attend this training!

Learn how to turn bad code into good code and become better, more professional PHP developers. No question goes unanswered when we pull up an editor and explain best practices while we code a sample application live.

The PHP Days Montréal are a one-of-a-kind classroom training experience where all three thePHP.cc trainers will be present at all times. As the training is scheduled right before the ConFoo conference and the topics covered complement its presentations, the PHP Days Montréal are the perfect preparation for attendees to get the most out of the conference.

Register now to secure your seat as the number of attendees is limited to ensure a beneficial learning environment.

Defined tags for this entry: , ,