Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion docs/usage/mocking-wp-action-and-filter-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,25 @@ final class MyClassTest extends TestCase
$this->assertConditionsMet();
}
}
```
```

## Asserting that actions and filters have been removed

Similarly, we can test that actions and filters are removed when expected, e.g.removing another plugin's admin notice. This is done using `WP_Mock::expectActionRemoved()` and `WP_Mock::expectFilterRemoved()`. Or conversely, we can confirm that they have _not_ been removed using `WP_Mock::expectActionNotRemoved()` and `WP_Mock::expectFilterNotRemoved()`. The latter functions are useful where a function being tested returns early in some scenarios, and we want to ensure that the hooks are not removed in that case. NB: omitting the priority does not catch any uses of `remove_action()` that use an explicit priority, for that case consider using `\WP_Mock\Functions::type( 'int' )`.

```php
use MyPlugin\MyClass;
use WP_Mock\Tools\TestCase as TestCase;

final class MyClassTest extends TestCase
{
public function testRemoveAction() : void
{
$classInstance = new MyClass();

WP_Mock::expectActionRemoved('admin_notices', 'invasive_admin_notice');

$classInstance->removeInvasiveAdminNotice();
}
}
```
93 changes: 93 additions & 0 deletions php/WP_Mock.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

use Mockery\Exception as MockeryException;
use Mockery\ExpectationInterface;
use WP_Mock\DeprecatedMethodListener;
use WP_Mock\Functions\Handler;
use WP_Mock\Matcher\FuzzyObject;
Expand Down Expand Up @@ -546,4 +547,96 @@ public static function getDeprecatedMethodListener(): DeprecatedMethodListener
{
return static::$deprecatedMethodListener;
}

/**
* Adds an expectation that an action should be removed.
*
* @param string $action the action hook name
* @param string|callable-string|callable|Type $callback the callable to be removed
* @param int|Type|null $priority the priority it should be registered at
*
* @return ExpectationInterface
* @throws InvalidArgumentException
*/
public static function expectActionRemoved(string $action, $callback, $priority = null)
{
$args = [$action, $callback];
if (!is_null($priority)) {
$args[] = $priority;
}

return self::userFunction('remove_action', [
'args' => $args,
'times' => 1,
]);
}

/**
* Adds an expectation that an action should not be removed.
*
* @param string $action the action hook name
* @param null|string|callable-string|callable|Type $callback the callable to be removed
* @param int|Type|null $priority optional priority for the registered callback that is being removed
*
* @return ExpectationInterface
* @throws InvalidArgumentException
*/
public static function expectActionNotRemoved(string $action, $callback, $priority = null)
{
$args = [$action, $callback];
if (!is_null($priority)) {
$args[] = $priority;
}

return self::userFunction('remove_action',[
'args' => $args,
'times' => 0,
]);
}

/**
* Adds an expectation that a filter should be removed.
*
* @param string $filter the filter name
* @param string|callable-string|callable|Type $callback the callable to be removed
* @param int|Type|null $priority the registered priority
*
* @return ExpectationInterface
* @throws InvalidArgumentException
*/
public static function expectFilterRemoved(string $filter, $callback, $priority = null)
{
$args = [$filter, $callback];
if (!is_null($priority)) {
$args[] = $priority;
}

return self::userFunction('remove_filter',[
'args' => $args,
'times' => 1,
]);
}

/**
* Adds an expectation that a filter should not be removed.
*
* @param string $filter the filter name
* @param null|string|callable-string|callable|Type $callback the callable to be removed
* @param int|Type|null $priority optional priority for the registered callback that is being removed
*
* @return ExpectationInterface
* @throws InvalidArgumentException
*/
public static function expectFilterNotRemoved(string $filter, $callback, $priority = null)
{
$args = [$filter, $callback];
if (!is_null($priority)) {
$args[] = $priority;
}

return self::userFunction('remove_filter',[
'args' => $args,
'times' => 0,
]);
}
}
56 changes: 56 additions & 0 deletions tests/Integration/WP_MockTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,60 @@ public function testCanExpectHooksNotAdded() : void

$this->assertConditionsMet();
}

/**
* @covers \WP_Mock::expectActionRemoved()
* @covers \WP_Mock::expectFilterRemoved()
*
* @return void
* @throws Exception
*/
public function testCanExpectHooksRemoved() : void
{
WP_Mock::expectActionRemoved('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10);
WP_Mock::expectFilterRemoved('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10);

WP_Mock::expectActionRemoved(
'wpMockTestActionWithCallbackAndAnyPriority',
'wpMockTestFunction',
\WP_Mock\Functions::type('int')
);
WP_Mock::expectFilterRemoved(
'wpMockTestFilterWithCallbackAndAnyPriority',
'wpMockTestFunction',
\WP_Mock\Functions::type('int')
);

WP_Mock::expectActionRemoved('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction');
WP_Mock::expectFilterRemoved('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction');

remove_action('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10);
remove_filter('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10);

remove_action('wpMockTestActionWithCallbackAndAnyPriority', 'wpMockTestFunction', rand(1,100));
remove_filter('wpMockTestFilterWithCallbackAndAnyPriority', 'wpMockTestFunction', rand(1,100));

remove_action('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction');
remove_filter('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction');

$this->assertConditionsMet();
}

/**
* @covers \WP_Mock::expectActionNotRemoved()
* @covers \WP_Mock::expectFilterNotRemoved()
*
* @return void
* @throws Exception
*/
public function testCanExpectHooksNotRemoved() : void
{
WP_Mock::expectActionNotRemoved('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10);
WP_Mock::expectFilterNotRemoved('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10);

WP_Mock::expectActionNotRemoved('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction');
WP_Mock::expectFilterNotRemoved('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction');

$this->assertConditionsMet();
}
}