Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit mock all methods of an abstract class

I have a PHPUnit test case directly deriving from PHPUnit_Framework_TestCase. In a test in this class I need to get a mock for some service object. This service object is of a type defined by an abstract base class. This base class holds both concrete and abstract methods. I want to get a full mock for the thing (ie all methods mocked out). My question is how to do this.

->getMock gives me an error since the abstract methods are not mocked, only the concrete ones

->getMockForAbstractClass mocks out the abstract methods but not the concrete ones

How do I mock them all out?

(I'm using PHPUnit 3.7.13)

like image 676
Jeroen De Dauw Avatar asked Apr 23 '13 14:04

Jeroen De Dauw


2 Answers

Just calling ->getMock('Class'); will mock all the methods on an object and implement all the abstract methods.

I'm not really sure where you went wrong but since it's so, seemingly, straight forward I wrote a sample.

If it doesn't work out for you I'd need a reproduce case of what you are trying to do

Sample

<?php

class mockTest extends PHPUnit_Framework_TestCase {

    // Without expectations
    public function testMocking() {
        $x = $this->getMock('MockMe');
        $this->assertNull($x->foo());
        $this->assertNull($x->bar());
    }

    // With expectations
    public function testMocking2() {
        $x = $this->getMock('MockMe');
        $x->expects($this->once())->method('foo')->will($this->returnValue(true));
        $x->expects($this->once())->method('bar')->will($this->returnValue(true));
        $this->assertTrue($x->foo());
        $this->assertTrue($x->bar());
    }

}

abstract class MockMe {

    abstract public function foo();

    public function bar() {
        return 1 + $this->foo();
    }

}

Produces

PHPUnit 3.7.13 by Sebastian Bergmann.

..

Time: 0 seconds, Memory: 6.25Mb

OK (2 tests, 5 assertions)
like image 166
edorian Avatar answered Oct 06 '22 15:10

edorian


It looks like $this->getMock(...) will fail for abstract classes if one or more of the abstract methods are not public.

If you don't want to use a derivative, you could add something like this to your test base class:

protected function getMockOfAbstractClass($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) {
    if ($methods !== null) {
        $methods = array_unique(array_merge($methods,
        $this->getMethods($originalClassName, $callAutoload)));
    }
    return parent::getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload);
}

private function getMethods($class, $autoload=true) {
    $methods = array();
    if (class_exists($class, $autoload) || interface_exists($class, $autoload)) {
        $reflector = new ReflectionClass($class);
        foreach ($reflector->getMethods( ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_ABSTRACT ) as $method) {
            $methods[] = $method->getName();
        }
    }
    return $methods;
}

This is adapted from: Mocking concrete method in abstract class using phpunit

like image 34
oliverk Avatar answered Oct 06 '22 15:10

oliverk