6

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)

Jeroen De Dauw
  • 10,321
  • 15
  • 56
  • 79
  • 1
    This has been implemented in later verions of PHPUnit as mentioned in this answer. http://stackoverflow.com/a/13365088/498699 – Schleis Jul 30 '14 at 16:23

3 Answers3

6

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)
edorian
  • 38,542
  • 15
  • 125
  • 143
  • 3
    I'm doing $this->getMock( 'SomeAbstractClass' ). Getting this: Fatal error: Class Mock_SomeAbstractClass_cbc14c8a contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (SomeAbstractClass::foo, SomeAbstractClass::bar) in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php(231) : eval()'d code on line 3302 – Jeroen De Dauw Apr 24 '13 at 14:00
  • Interestingly enough your example code does work just fine for me. The abstract class in my case is quite complex and implements some interface. Perhaps it is causing some bug in PHPUnit? My workaround for now is to just reference one of its derivatives in the getMock method call. – Jeroen De Dauw Apr 24 '13 at 14:10
  • It's of course always possible. Or maybe it can't find/load one of the interfaces and can't figure out the methods beforehand (or something!). If you can reproduce the error with code that you can share a bug report surely would be welcome :) – edorian Apr 24 '13 at 15:57
4

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

Community
  • 1
  • 1
oliverk
  • 41
  • 1
0

With getMockForAbstractClass, concrete methods are not mocked by default. To mock concrete methods, use the 7th parameter ($mockedMethods).

Diana Eftaiha
  • 73
  • 2
  • 7