phptestingmockingphpunitprophecy

Prophecy - Expect that no method will be called on an object


right now I'm using the following code in PHPUnit to expect that no method is called on a mock:

$object = $this->createMock(ClassA:class);
$object->expects($this->never())->method($this->anything());

So far I haven't found a way to achieve the same result in Prophecy. I only have been able to test assumptions on specific methods so far and not on all methods like in the sample above.

Currently I'm using the following custom assertion to test whether no method has been called. Prophecy's ObjectProphecy exposes a method to get all calls to a specific function so I'm using reflection to get all methods on a class and than each call for each method. If the array of calls is empty afterwards, I know that no method has been called. The method now looks like this:

public function assertNoMethodHasBeenCalled(ObjectProphecy $prophecy, string $className)
{
    $methods = get_class_methods($className);

    $calls = [];
    foreach ($methods as $method) {
        $reflectionMethod = new \ReflectionMethod($className, $method);
        $numArgs = count($reflectionMethod->getParameters());
        $calls = array_merge(
            $calls,
            $prophecy->findProphecyMethodCalls(
                $method,
                new ArgumentsWildcard(array_fill(0, $numArgs, Argument::any()))
            )
        );
    }

    $this->assertEmpty($calls);
}

It's working for my limited sample size so far but I'm not happy with it. I feel like there should be an easier way to achieve the same result.


Solution

  • To my knowledge, there is no simple to achieve this with Prophecy.

    Prophecy works differently from PHPUnit's mocks when it comes to expectations. As soon as you set up an expectation, every call to your mock that isn't a prediction or a promise, will fail the test. Given the following object prophecy:

    $prophecy = $this->prophesize(ClassA::class);
    $prophecy->methodA()->shouldNotBeCalled();
    
    $objectA = $prophecy->reveal();
    

    Both

    $objectA->methodA();
    

    and

    $objectA->methodB();
    

    will fail the test.

    So, when you don't set up any expectations, you have to check for calls manually like you did.