Expectation on "times" required for `withArgs`
coreyworrell opened this issue · 10 comments
What am I doing wrong here?
class Example
{
public function __construct()
{
add_filter('template_include', [$this, 'template']);
}
public function template(string $template): string
{
return 'something';
}
}
class ExampleTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Brain\Monkey\setUp();
}
protected function tearDown(): void
{
Brain\Monkey\tearDown();
parent::tearDown();
}
public function testExample()
{
Brain\Monkey\Filters\expectAdded('template_include')
->with('Example->template()');
new Example;
}
}
Returns
There was 1 error:
1) ExampleTest::testExample
Mockery\Exception\NoMatchingExpectationException: No matching handler found for Mockery_0::add_filter_template_include([0 => object(Example), 1 => 'template'], 10, 1). Either the method was unexpected or its arguments matched no expected argument list for this method
/.../vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:92
/.../vendor/brain/monkey/src/Hook/HookExpectationExecutor.php:127
/.../vendor/brain/monkey/src/Hook/HookExpectationExecutor.php:64
/.../vendor/brain/monkey/inc/wp-hook-functions.php:35
/.../src/Example.php:24 (add_filter('template_include', [$this, 'template']))
/.../tests/ExampleTest.php:50 (new Example)
Instead of this:
Brain\Monkey\Filters\expectAdded('template_include')
->with('Example->template()');
try this:
static::assertTrue( has_filter( 'template_include', 'Example->template()' ) );
(Or [ Example::class, 'template' ]
etc.)
This needs to go after you instantiated the object, though.
Yes that works, but shouldn't the expectation work as well? It seems to be the preferred way to test actions/filters.
I believe Brain\Monkey\Filters\expectAdded()->with()
(i.e., essentially a Mockery expectation) does not make use of CallbackStringForm
, which has_filter
(i.e., internally the HookStorage
) does.
Is this assumption corect @gmazzap? And if so, has this been done on purpose?
I saw a version from a blog article that used
expectAdded('filter')->with(function ($cb) {
return $cb[0] instanceof Example && $cb[1] === 'template';
});
But that also gave me the same result.
@coreyworrell, as @tfrommen said, the "string representation" of a callback only works with core functions: has_action
, has_filter
, did_action
... It does not work when using ->with
because it is a method from Mockery that I don't control.
When using Mockery methods you have to stick with what Mockery offers.
The latest snippet you posted:
expectAdded('filter')->with(function ($cb) {
return $cb[0] instanceof Example && $cb[1] === 'template';
});
Will not work because that is telling Mockery to expect the closure as the only argument, which can never be true, because the closure is instantiated right in inside the test.
The correct way should be:
expectAdded('filter')->withArgs(function ($cb) {
return $cb[0] instanceof Example && $cb[1] === 'template';
});
In fact, withArgs
accepts either the array of arguments or a closure to verify them. See http://docs.mockery.io/en/latest/reference/expectations.html#argument-matching-with-closures
Right now, I can't test if in Brain Monkey that works as expected, if you test it please let us know.
Thanks!
Thanks @gmazzap! That's what I was missing, difference between with
and withArgs
. I refactored my test in a way that didn't require any calls to add_filter
, but this looks like the correct way if I need to do so later.
@gmazzap Just tested this again, it does not seem to work. The test passes no matter what. Even this passes:
expectAdded('wp_dashboard_setup')->withArgs(function () {
return false;
});
Thanks @coreyworrell
I found the issue. When using withArgs()
you have to also add an expectation on the times something should happen.
For example:
expectAdded('template_include')
->once() // <~ this
->withArgs(function() { return false; });
or even:
expectAdded('template_include')
->atLeast()->once() // <~ this
->withArgs(function() { return false; });
When using with
to verify arguments, that is not required.
At the moment I've no time to dig into the reasons of this unconsistence, but I'll try to see if it is something that can be fixed in Brain Monkey or depends on Mockery.
If not fixable in Brain Monkey, it should at least be documented.
Thanks for reporting.
Thanks for looking into this and for the fix!