Arnold Daniels
Arnold Daniels

Reputation: 16563

How to make PHPUnit mock fail on calling unconfigured methods?

Is it possible to have PHPUnit fail when any unconfigured method is called on a mock object?

Example;

$foo = $this->createMock(Foo::class);
$foo->expects($this->any())->method('hello')->with('world');

$foo->hello('world');
$foo->bye();

This test will succeed. I would like it to fail with

Foo::bye() was not expected to be called. 

P.S. The following would work, but this means I'd have to list all configured methods in the callback. That's not a suitable solution.

$foo->expects($this->never())
    ->method($this->callback(fn($method) => $method !== 'hello'));

Upvotes: 4

Views: 354

Answers (2)

Matt Janssen
Matt Janssen

Reputation: 1653

What you're looking for is to disable the automatic generation of return values when no return value is configured. Once you disable this, if a method is called that you didn't explicitly define, you'll get the following test failure:

Return value inference disabled and no expectation set up for Foo\Bar::myMethod()

Starting with PHPUnit 11.1.0, you can add the following attribute to your test class:

use PHPUnit\Framework\Attributes\DisableReturnValueGenerationForTestDoubles;

#[DisableReturnValueGenerationForTestDoubles]
class MyTest extends TestCase

Prior to PHPUnit 10, you could override this method in your test class:

public function getMockBuilder(string $className): MockBuilder
{
    return parent::getMockBuilder($className)->disableAutoReturnValueGeneration();
}

To do the same in PHPUnit 10 or 11.0, I suppose you'd have to create some custom mock method (eg. createStrictMock()), as most of the base methods were made final in 10.0 and can no longer be overridden.

Upvotes: 1

Arnold Daniels
Arnold Daniels

Reputation: 16563

This is done by disabling auto-return value generation.

$foo = $this->getMockBuilder(Foo::class)
    ->disableAutoReturnValueGeneration()
    ->getMock();

$foo->expects($this->any())->method('hello')->with('world');

$foo->hello('world');
$foo->bye();

This will result in

Return value inference disabled and no expectation set up for Foo::bye()

Note that it's not required for other methods (like hello) to define a return method.

Upvotes: 6

Related Questions