30

For the code below,

$mockObject->expects($this->at(0))
           ->method('search')
           ->with($searchConfig)
           ->will($this->returnValue([]));

This line will automatic make a assertensure that when it call method search it must contain $searchConfig parameters. In this case, we have to provide totally matched $searchConfig but sometime it is hard if it is an array or an object.

Are there any possible way to let PHPUnit call to some specific method to assert that it contains arguments pass in a method as we want?

For example, I may create closure function to assert as below instead of using ->with() method

function ($config){
    $this->assertFalse(isset($config['shouldnothere']));
    $this->assertTrue($config['object']->isValidValue());
}

2 Answers 2

62

You can use ->with($this->callback()) and pass in a closure to perform more complex assertions on the argument.

From the PHPUnit Docs

The callback() constraint can be used for more complex argument verification. This constraint takes a PHP callback as its only argument. The PHP callback will receive the argument to be verified as its only argument and should return TRUE if the argument passes verification and FALSE otherwise.

Example 10.13: More complex argument verification

getMock('Observer', array('reportError'));

    $observer->expects($this->once())
             ->method('reportError')
             ->with($this->greaterThan(0),
                    $this->stringContains('Something'),
                    $this->callback(function($subject){
                      return is_callable(array($subject, 'getName')) &&
                             $subject->getName() == 'My subject';
                    }));

    $subject = new Subject('My subject');
    $subject->attach($observer);

    // The doSomethingBad() method should report an error to the observer
    // via the reportError() method
    $subject->doSomethingBad();
} } ?>

So your test would become:

$mockObject->expects($this->at(0))
->method('search')
->with($this->callback(
    function ($config){
        if(!isset($config['shouldnothere']) && $config['object']->isValidValue()) {
            return true;
        }
        return false;
    })
->will($this->returnValue([]));
Sign up to request clarification or add additional context in comments.

3 Comments

I always forget to use return true when validating multiple arguments.
Btw, if your function has more then 1 argument, then the this->callback() only applies for the first argument. So you need to define a callback for each argument
Thanks for this, that on had me scratching my head for a bit.
3

You can use Mockery for it

Say I have a code

$productValidator = app(ProductValidator:class)->setProduct($productIn);

That would be the test code

use Mockery;

...

    $productValidator            
        ->shouldReceive('setProduct')->once()
        ->with(Mockery::type(Product::class))
        ->with(Mockery::on(function($productIn) use ($productId) {
                return $productIn->id === $productId;
            }))
        ->andReturn($productValidator);

Tested on phpunit "version": "8.5.8"

1 Comment

$this->callback() was not working for me in a Laravel 10 project. This response worked for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.