Skip to content

Commit

Permalink
Add AnyInstance matcher. Fix #160 (#161)
Browse files Browse the repository at this point in the history
Co-authored-by: Fulvio Notarstefano <[email protected]>
  • Loading branch information
BrianHenryIE and unfulvio-godaddy authored Oct 27, 2022
1 parent b647631 commit d058417
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased] - TBD

### Added
- AnyInstance matcher

## [0.4.2] - 2019-03-15
### Added
- **Minor Filter/Action/Hook Assertion Bugfix**
Expand Down
2 changes: 1 addition & 1 deletion CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The following individuals are responsible for curating the list of issues, respo

Thank you to all the people who have already contributed to this repository via bug reports, code, design, ideas, project management, translation, testing, etc.

[Eric Mann (@ericmann)](https://github.com/ericmann), [John P. Bloch (@johnpbloch)](https://github.com/johnpbloch), [Nicolas VINCENT (@nicolqs)](https://github.com/nicolqs), [Alex Khadiwala (@khadiwaa)](https://github.com/khadiwaa), [Kat Hagan (@codebykat)](https://github.com/codebykat), [Jeff Sebring (@jeffsebring)](https://github.com/jeffsebring), [Giuseppe Mazzapica (@gmazzap)](https://github.com/gmazzap), [Luís Rodrigues (@goblindegook)](https://github.com/goblindegook), [Steve Grunwell (@stevegrunwell)](https://github.com/stevegrunwell), [Sudar Muthu (@sudar)](https://github.com/sudar), [Thorsten Frommen (@tfrommen)](https://github.com/tfrommen), [Darshan Sawardekar (@dsawardekar)](https://github.com/dsawardekar), [Gary Jones (@GaryJones)](https://github.com/GaryJones), [Payton Swick (@sirbrillig)](https://github.com/sirbrillig), [Pete Nelson (@petenelson)](https://github.com/petenelson), [Taylor Lovett (@tlovett1)](https://github.com/tlovett1), [Mathieu Hays (@mathieuhays)](https://github.com/mathieuhays), [Luke Woodward (@lkwdwrd)](https://github.com/lkwdwrd), [Chris Marslender (@cmmarslender)](https://github.com/cmmarslender), [Brian Watson (@bswatson)](https://github.com/bswatson), [Krody Robert (@krodyrobi)](https://github.com/krodyrobi), [Chris Wiseman](), [Andrea Sciamanna](), [Patrick Safarov (@psafarov)](https://github.com/psafarov), [Aaron (@ocularrhythm)](https://github.com/ocularrhythm), [Ramy Deeb (@rdeeb)](https://github.com/rdeeb), [Christopher Watts (@rocketcrazy07)](https://github.com/rocketcrazy07), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul).
[Eric Mann (@ericmann)](https://github.com/ericmann), [John P. Bloch (@johnpbloch)](https://github.com/johnpbloch), [Nicolas VINCENT (@nicolqs)](https://github.com/nicolqs), [Alex Khadiwala (@khadiwaa)](https://github.com/khadiwaa), [Kat Hagan (@codebykat)](https://github.com/codebykat), [Jeff Sebring (@jeffsebring)](https://github.com/jeffsebring), [Giuseppe Mazzapica (@gmazzap)](https://github.com/gmazzap), [Luís Rodrigues (@goblindegook)](https://github.com/goblindegook), [Steve Grunwell (@stevegrunwell)](https://github.com/stevegrunwell), [Sudar Muthu (@sudar)](https://github.com/sudar), [Thorsten Frommen (@tfrommen)](https://github.com/tfrommen), [Darshan Sawardekar (@dsawardekar)](https://github.com/dsawardekar), [Gary Jones (@GaryJones)](https://github.com/GaryJones), [Payton Swick (@sirbrillig)](https://github.com/sirbrillig), [Pete Nelson (@petenelson)](https://github.com/petenelson), [Taylor Lovett (@tlovett1)](https://github.com/tlovett1), [Mathieu Hays (@mathieuhays)](https://github.com/mathieuhays), [Luke Woodward (@lkwdwrd)](https://github.com/lkwdwrd), [Chris Marslender (@cmmarslender)](https://github.com/cmmarslender), [Brian Watson (@bswatson)](https://github.com/bswatson), [Krody Robert (@krodyrobi)](https://github.com/krodyrobi), [Chris Wiseman](), [Andrea Sciamanna](), [Patrick Safarov (@psafarov)](https://github.com/psafarov), [Aaron (@ocularrhythm)](https://github.com/ocularrhythm), [Ramy Deeb (@rdeeb)](https://github.com/rdeeb), [Christopher Watts (@rocketcrazy07)](https://github.com/rocketcrazy07), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul), [Brian Henry (@BrianHenryIE)](https://github.com/BrianHenryIE).

## Libraries

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,12 @@ public function test_special_function() {

It's important to note that the `$priority` and `$parameter_count` arguments (parameters 3 and 4 for both `add_action()` and `add_filter()`) are significant. If `special_function()` were to call `add_action( 'save_post', 'special_save_post', 99, 3 )` instead of the expected `add_action( 'save_post', 'special_save_post', 10, 2 )`, our test would fail.

If the actual instance of an expected class cannot be passed, `AnyInstance` can be used:

```php
\WP_Mock::expectFilterAdded( 'the_content', array( new \WP_Mock\Matcher\AnyInstance( Special::class ), 'the_content' ) );
```

#### Asserting that actions and filters are applied

Now that we're testing whether or not we're adding actions and/or filters, the next step is to ensure our code is calling those hooks when expected.
Expand Down
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
"php" : "7.3"
}
},
"autoload-dev" : {
"classmap": ["tests"]
},
"scripts": {
"test:behat": "behat",
"test:phpunit": "phpunit",
Expand Down
4 changes: 4 additions & 0 deletions php/WP_Mock/Hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
namespace WP_Mock;


use WP_Mock\Matcher\AnyInstance;

abstract class Hook {
/** @var string Hook name */
protected $name;
Expand All @@ -25,6 +27,8 @@ protected function safe_offset( $value ) {
return 'null';
} elseif ( is_scalar( $value ) ) {
return $value;
}elseif( $value instanceof AnyInstance ) {
return (string) $value;
} elseif ( is_object( $value ) ) {
return spl_object_hash( $value );
} elseif ( is_array( $value ) ) {
Expand Down
12 changes: 11 additions & 1 deletion php/WP_Mock/HookedCallback.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace WP_Mock;

use WP_Mock\Matcher\AnyInstance;
class HookedCallback extends Hook {

protected $type = 'filter';
Expand All @@ -18,6 +19,15 @@ public function react( $callback, $priority, $argument_count ) {
\WP_Mock::addHook( $this->name );

$safe_callback = $this->safe_offset( $callback );

if( is_array( $callback ) ) {
$any_instance_callback = array( new AnyInstance( $callback[0] ), $callback[1] );
$safe_any_instance_callback = $this->safe_offset( $any_instance_callback );
if( ! empty( $this->processors[ $safe_any_instance_callback ])) {
$safe_callback = $safe_any_instance_callback;
}
}

if (
empty( $this->processors[ $safe_callback ] ) ||
empty( $this->processors[ $safe_callback ][ $priority ] ) ||
Expand All @@ -29,7 +39,7 @@ public function react( $callback, $priority, $argument_count ) {
return null;
}

return $this->processors[ $this->safe_offset( $callback ) ][ $priority ][ $argument_count ]->react();
return $this->processors[ $safe_callback ][ $priority ][ $argument_count ]->react();
}

protected function new_responder() {
Expand Down
71 changes: 71 additions & 0 deletions php/WP_Mock/Matcher/AnyInstance.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
// Match on class type.

namespace WP_Mock\Matcher;

use Mockery\Exception;
use Mockery\Matcher\MatcherAbstract;

class AnyInstance extends FuzzyObject {

/**
* @param string|object $expected A classname or instance of a class whose type should match.
*
* @throws \Mockery\Exception
*/
public function __construct( $expected = null ) {
if( is_string( $expected ) && class_exists( $expected ) ) {
$reflectedExpected = new \ReflectionClass( $expected );
$expectedInstance = $reflectedExpected->newInstanceWithoutConstructor();
} elseif ( is_object( $expected ) ) {
$expectedInstance = $expected;
} else {
throw new Exception( 'AnyInstance matcher can only match objects!' );
}

parent::__construct($expectedInstance);
}

/**
* Check if the actual value matches the expected.
* Actual passed by reference to preserve reference trail (where applicable)
* back to the original method parameter.
*
* @param mixed $actual
*
* @return bool
*/
public function match( &$actual ) {
if ( ! is_object( $actual ) ) {
return false;
}

if( $actual instanceof \Closure ) {
return false;
}

if( get_class( $actual ) === get_class( $this->_expected ) ) {
return true;
}

// parent::haveCommonAncestor() expects two objects.
$reflectedExpected = new \ReflectionClass( $this->_expected );
$expectedInstance = $reflectedExpected->newInstanceWithoutConstructor();
if ( ! $this->haveCommonAncestor( $actual, $expectedInstance ) ) {
return false;
}

return true;
}

/**
* Return a string representation of this Matcher
*
* @return string
*/
public function __toString() {
$classname = get_class($this->_expected);
return "<AnyInstance[{$classname}]>";
}

}
4 changes: 2 additions & 2 deletions php/WP_Mock/Matcher/FuzzyObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,6 @@ protected function haveCommonAncestor( $object1, $object2 ) {
}
$inheritance1 = class_parents( $class1 );
$inheritance2 = class_parents( $class2 );
return (bool) array_intersect_assoc( $inheritance1, $inheritance2 );
}
return in_array( $class1, $inheritance2 ) || in_array( $class2, $inheritance1 );
}
}
113 changes: 113 additions & 0 deletions tests/WP_Mock/Matcher/AnyInstanceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

namespace WP_Mock\Matcher;

class AnyInstanceTest extends \PHPUnit\Framework\TestCase
{

/**
* @covers \WP_Mock\Matcher\AnyInstance::match
*/
public function testExactClassInstanceMatchesTrue()
{
$sut = new AnyInstance(new SampleClass());

$exactClassAction = new SampleClass();

$result = $sut->match($exactClassAction);

$this->assertTrue($result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::match
*/
public function testExactClassStringMatchesTrue()
{
$sut = new AnyInstance(SampleClass::class);

$exactClassAction = new SampleClass();

$result = $sut->match($exactClassAction);

$this->assertTrue($result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::match
*/
public function testSubClassMatchesTrue()
{
$sut = new AnyInstance(SampleClass::class);

$subClassAction = new SampleSubClass();

$result = $sut->match($subClassAction);

$this->assertTrue($result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::match
*/
public function testWrongClassMatchesFalse()
{
$sut = new AnyInstance(SampleClass::class);

$wrongClassAction = new \stdClass();

$result = $sut->match($wrongClassAction);

$this->assertFalse($result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::match
*/
public function testClosureMatchesFalse()
{
$sut = new AnyInstance(SampleClass::class);

$closureAction = function () {
};

$result = $sut->match($closureAction);

$this->assertFalse($result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::match
*/
public function testStringFunctionMatchesFalse()
{
$sut = new AnyInstance(SampleClass::class);

$stringFunctionAction = 'action_name';

$result = $sut->match($stringFunctionAction);

$this->assertFalse($result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::__toString
*/
public function testToString() {
$sut = new AnyInstance(SampleClass::class);

$result = "$sut";

$this->assertEquals("<AnyInstance[WP_Mock\Matcher\SampleClass]>", $result);
}

/**
* @covers \WP_Mock\Matcher\AnyInstance::__construct
*/
public function testCannotConstructWithoutObject() {
$this->expectException(\Exception::class);

new AnyInstance('NotAClass' );
}
}

7 changes: 7 additions & 0 deletions tests/WP_Mock/Matcher/SampleClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace WP_Mock\Matcher;

class SampleClass {
public function action(){}
}
7 changes: 7 additions & 0 deletions tests/WP_Mock/Matcher/SampleSubClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace WP_Mock\Matcher;

class SampleSubClass extends SampleClass {
public function action(){}
}

0 comments on commit d058417

Please sign in to comment.