views:

312

answers:

2

While trying to get a legacy codebase under test, I've come across an object that does the following:

class Foo
{
    public function __construct($someargs)
    {
        $this->bar = new Bar();
        // [lots more code]
    }
}

Bar in this instance has a constructor that does some Bad Things e.g. connecting to a database. I'm trying to concentrate on getting this Foo class under test so changed it to something like this:

class Foo
{
    public function __construct($someargs)
    {
        $this->bar = $this->getBarInstance();
        // [lots more code]
    }

    protected function getBarInstance()
    {
        return new Bar();
    }
}

And have attempted to test it via the following PHPUnit test:

class FooTest extends PHPUnit_Framework_TestCase
{
    public function testInstance()
    {

        $bar = $this->getMock('Bar');
        $foo = $this->getMock('Foo', array('getBarInstance'));
        $foo->expects($this->any())
            ->method('getBarInstance')
            ->will($this->returnValue($bar));

    }

}

However this doesn't work - the constructor of Foo() is called before my ->expects() is added, so the mocked getBarInstance() method returns a null.

Is there any way of unlinking this dependency without having to refactor the way the class uses constructors?

+2  A: 

Use the $callOriginalConstructor argument of getMock(). Set it to false. It's the fifth argument of the method. Look it up here: http://www.phpunit.de/manual/current/en/api.html#api.testcase.tables.api

Actually, hold on. You want to pass a mock to a mock? If you really want this, then use the third argument of getMock which represents the arguments to the constructor. There you can pass the mock of Bar to the mock of Foo.

Ionuț G. Stan
But the constructor does 'stuff', should that be split out into a separate method?
Ciaran McNulty
I've just edited the answer. You can pass a mock to the mock, but I don't really see the point.
Ionuț G. Stan
And yes, if the constructor does stuff you should move that code into a separate method. Constructor should usually just set up the state of the object, like setting the instance fields to certain values, usually passed as arguments to the constructor.
Ionuț G. Stan
I'm doing a partial mock of Foo and only overriding the method that contains the dependency, as I've not got a better way of injecting in the Bar yet (hopefully once this is under test I can refactor it a bit).
Ciaran McNulty
Use the third argument of `getMock('Foo')` in order to pass the mock of `Bar`. It should work.
Ionuț G. Stan
A: 

Check this article out:

http://www.potstuck.com/2009/01/08/php-dependency-injection/

sunwukung