Stubbing Hard-Coded Dependencies

Sebastian Bergmann » 16 February 2010 » in Articles » 1 Comment

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

In a unit test, mock objects can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is difficult or impossible to incorporate into a unit test.

A mock object can be used anywhere in the program where the program expects an object of the mocked class. However, this only works as long as the object can be passed into the context where the original object is used.

Consider the following example:

<?php
require_once 'Bar.php';
 
class Foo
{
    public function doSomething()
    {
        // ...
 
        $bar = new Bar;
        $bar->doSomethingElse();
 
        // ...
 
        return TRUE;
    }
}
?>
<?php
class Bar
{
    public function doSomethingElse()
    {
        print '*';
    }
}
?>

With the code above, it is impossible to run a unit test for the Foo::doSomething() method without also creating an object of Bar. As the method creates the object of Bar itself, we cannot inject a mock object in its stead.

In a perfect world, code such as the above could be refactored using Dependency Injection:

<?php
require_once 'Bar.php';
 
class Foo
{
    public function doSomething(Bar $bar = NULL)
    {
        if ($bar === NULL) {
            $bar = new Bar;
        }
 
        // ...
 
        $bar->doSomethingElse();
 
        // ...
 
        return TRUE;
    }
}
?>

Unfortunately, this is not always possible (not because of technical reasons, though).

This is where the set_new_overload() function that is provided by the test_helpers extension for the PHP Interpreter comes into play. It can be used to register a callback that is automatically invoked when the new operator is executed:

<?php
require_once 'Foo.php';
 
class FooTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->getMock(
          'Bar',                    /* name of class to mock     */
          array('doSomethingElse'), /* list of methods to mock   */
          array(),                  /* constructor arguments     */
          'BarMock'                 /* name for mocked class     */
        );
 
        set_new_overload(array($this, 'newCallback'));
    }
 
    protected function tearDown()
    {
        unset_new_overload();
    }
 
    protected function newCallback($className)
    {
        switch ($className) {
            case 'Bar': return 'BarMock';
            default:    return $className;
        }
    }
 
    public function testDoSomething()
    {
        $foo = new Foo;
        $this->assertTrue($foo->doSomething());
    }
}
?>

Lets run this unit test:

PHPUnit 3.4.10 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 7.50Mb

OK (1 test, 2 assertions)

Note that there is no * (printed from Bar::doSomethingElse()) in the output above.

Defined tags for this entry: ,

Trackback specific URI for this entry

1 Comment to "Stubbing Hard-Coded Dependencies"

Display comments as (Linear | Threaded)
  1. conf
    16/02/2010 at 09:01 Permalink
    Wow! That's really a cool feature for testing old and ugly legacy code. I'll give it a try, thanks!

    Reply

5 Trackbacks to "Stubbing Hard-Coded Dependencies"

  1. Sebastian Bergmann 16/02/2010 at 08:20
    This article is part of a series on testing untestable code: Testing private methods Testing code that uses singletons Stubbing static methods Stubbing hard-coded dependencies In a unit test, mock objects can simulate the behavior of complex, real
  2. Sebastian Bergmann 16/02/2010 at 08:21
    This article is part of a series on testing untestable code: Testing private methods Testing code that uses singletons Stubbing static methods Stubbing hard-coded dependencies With PHPUnit 3.5 it will be possible to stub and mock static methods. C
  3. Sebastian Bergmann 16/02/2010 at 08:21
    This article is part of a series on testing untestable code: Testing private methods Testing code that uses singletons Stubbing static methods Stubbing hard-coded dependencies I frequently quote Miško Hevery with "It is hard to test code that uses
  4. Sebastian Bergmann 16/02/2010 at 08:21
    This article is part of a series on testing untestable code: Testing private methods Testing code that uses singletons Stubbing static methods Stubbing hard-coded dependencies No, not those privates. If you need help with those, this book might hel
  5. blog.tolleiv.de 08/05/2010 at 18:20

Add Comment


To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Submitted comments will be subject to moderation before being displayed.