Complex Argument Matching With Mockery::on¶
When we need to do a more complex argument matching for an expected method call,
the \Mockery::on() matcher comes in really handy. It accepts a closure as an
argument and that closure in turn receives the argument passed in to the method,
when called. If the closure returns true, Mockery will consider that the
argument has passed the expectation. If the closure returns false, or a
“falsey” value, the expectation will not pass.
The \Mockery::on() matcher can be used in various scenarios — validating
an array argument based on multiple keys and values, complex string matching…
Say, for example, we have the following code. It doesn’t do much; publishes a
post by setting the published flag in the database to 1 and sets the
published_at to the current date and time:
<?php
namespace Service;
class Post
{
public function __construct($model)
{
$this->model = $model;
}
public function publishPost($id)
{
$saveData = [
'post_id' => $id,
'published' => 1,
'published_at' => gmdate('Y-m-d H:i:s'),
];
$this->model->save($saveData);
}
}
In a test we would mock the model and set some expectations on the call of the
save() method:
<?php
$postId = 42;
$modelMock = \Mockery::mock('Model');
$modelMock->shouldReceive('save')
->once()
->with(\Mockery::on(function ($argument) use ($postId) {
$postIdIsSet = isset($argument['post_id']) && $argument['post_id'] === $postId;
$publishedFlagIsSet = isset($argument['published']) && $argument['published'] === 1;
$publishedAtIsSet = isset($argument['published_at']);
return $postIdIsSet && $publishedFlagIsSet && $publishedAtIsSet;
}));
$service = new \Service\Post($modelMock);
$service->publishPost($postId);
\Mockery::close();
The important part of the example is inside the closure we pass to the
\Mockery::on() matcher. The $argument is actually the $saveData argument
the save() method gets when it is called. We check for a couple of things in
this argument:
- the post ID is set, and is same as the post ID we passed in to the
publishPost()method, - the
publishedflag is set, and is1, and - the
published_atkey is present.
If any of these requirements is not satisfied, the closure will return false,
the method call expectation will not be met, and Mockery will throw a
NoMatchingExpectationException.
Note
This cookbook entry is an adaption of the blog post titled “Complex argument matching in Mockery”, published by Robert Basic on his blog.