views:

66

answers:

1

Hi, I'm writing a unit test for a class method that calls another class's method using a mock, only the method that needs to be called is declared as final, so PHPUnit is unable to mock it. Is there a different approach I can take?

example:

class to be mocked

class Class_To_Mock
{
    final public function needsToBeCalled($options)
    {
        ...
    }
}

my test case

class MyTest extends PHPUnit_Framework_TestCase
{
    public function testDoSomething()
    {
        $mock = $this->getMock('Class_To_Mock', array('needsToBeCalled'));
        $mock->expects($this->once())
             ->method('needsToBeCalled')
             ->with($this->equalTo(array('option'));
    }
}

Edit: If using the solution provided by Mike B and you have a setter/getter for the object you're mocking that does type checking (to ensure the correct object was passed into the setter), you'll need to mock the getter on the class you're testing and have it return the other mock.

example:

class to be mocked

class Class_To_Mock
{
    final public function needsToBeCalled($options)
    {
        ...
    }
}

mock

class Class_To_MockMock
{
    public function needsToBeCalled($options)
    {
        ...
    }
}

class to be tested

class Class_To_Be_Tested
{
    public function setClassToMock(Class_To_Mock $classToMock)
    {
        ...
    }

    public function getClassToMock()
    {
        ...
    }

    public function doSomething()
    {
        $this->getClassToMock()
             ->needsToBeCalled(array('option'));
    }
}

my test case

class MyTest extends PHPUnit_Framework_TestCase
{
    public function testDoSomething()
    {
        $classToTest = $this->getMock('Class_To_Be_Tested', array('getClassToMock'));

        $mock = $this->getMock('Class_To_MockMock', array('needsToBeCalled'));

        $classToTest->expects($this->any())
                    ->method('getClassToMock')
                    ->will($this->returnValue($mock));

        $mock->expects($this->once())
             ->method('needsToBeCalled')
             ->with($this->equalTo(array('option'));

        $classToTest->doSomething();
    }
}
+2  A: 

I don't think PHPUnit supports stubbing/mocking of final methods. You may have to create your own stub for this situation and do some extension trickery:

class myTestClassMock {
  public function needsToBeCalled() {
    $foo = new Class_To_Mock();
    $result = $foo->needsToBeCalled();
    return array('option');
  }
}

Found this in the PHPUnit Manual under Chapter 11. Test Doubles

Limitations

Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.

Mike B
Ah, missed that in the manual, thanks
rr
There's an extra step you need if the class you're testing relies on a getter/setter that does type checking for the class/method being mocked. I added this to the original post.
rr