From f637498c56f346a9c0428fb27ef04a3dca892770 Mon Sep 17 00:00:00 2001 From: Martin Jonas Date: Wed, 24 Feb 2021 19:22:42 +0100 Subject: [PATCH] Configurable default matchers --- CHANGELOG.md | 1 + library/Mockery/Configuration.php | 41 +++++++++++++ library/Mockery/Expectation.php | 6 ++ tests/Mockery/DefaultMatchersTest.php | 88 +++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 tests/Mockery/DefaultMatchersTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 94e17e3dd..75e172625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 1.4.4 (TBC) * Add method that allows defining a set of arguments the mock should yield #1133 +* Added option to configure default matchers for objects `\Mockery::getConfiguration()->setDefaultMatcher($class, $matcherClass)` #1120 ## 1.4.3 (2021-02-24) * Fixes calls to fetchMock before initialisation #1113 diff --git a/library/Mockery/Configuration.php b/library/Mockery/Configuration.php index e5272874a..bce05f3c5 100644 --- a/library/Mockery/Configuration.php +++ b/library/Mockery/Configuration.php @@ -74,6 +74,13 @@ public function __construct() */ protected $_objectFormatters = array(); + /** + * Default argument matchers + * + * @var array + */ + protected $_defaultMatchers = array(); + /** * Set boolean to allow/prevent mocking of non-existent methods * @@ -239,4 +246,38 @@ public function getObjectFormatter($class, $defaultFormatter) } return $defaultFormatter; } + + /** + * @param string $class + * @param string $matcherClass + */ + public function setDefaultMatcher($class, $matcherClass) + { + if (!is_a($matcherClass, \Mockery\Matcher\MatcherAbstract::class, true) && + !is_a($matcherClass, \Hamcrest\Matcher::class, true) && + !is_a($matcherClass, \Hamcrest_Matcher::class, true) + ) { + throw new \InvalidArgumentException( + "Matcher class must be either Hamcrest matcher or extend \Mockery\Matcher\MatcherAbstract, " . + "'$matcherClass' given." + ); + } + $this->_defaultMatchers[$class] = $matcherClass; + } + + public function getDefaultMatcher($class) + { + $parentClass = $class; + do { + $classes[] = $parentClass; + $parentClass = get_parent_class($parentClass); + } while ($parentClass); + $classesAndInterfaces = array_merge($classes, class_implements($class)); + foreach ($classesAndInterfaces as $type) { + if (isset($this->_defaultMatchers[$type])) { + return $this->_defaultMatchers[$type]; + } + } + return null; + } } diff --git a/library/Mockery/Expectation.php b/library/Mockery/Expectation.php index 497224592..06e1c04d7 100644 --- a/library/Mockery/Expectation.php +++ b/library/Mockery/Expectation.php @@ -389,6 +389,12 @@ protected function _matchArg($expected, &$actual) return true; } } + if (is_object($expected)) { + $matcher = \Mockery::getConfiguration()->getDefaultMatcher(get_class($expected)); + if ($matcher !== null) { + $expected = new $matcher($expected); + } + } if ($expected instanceof \Mockery\Matcher\MatcherAbstract) { return $expected->match($actual); } diff --git a/tests/Mockery/DefaultMatchersTest.php b/tests/Mockery/DefaultMatchersTest.php new file mode 100644 index 000000000..9d5050905 --- /dev/null +++ b/tests/Mockery/DefaultMatchersTest.php @@ -0,0 +1,88 @@ +value === $this->_expected->value; + } + + public function __toString() + { + return ""; + } +} + +interface CustomValueObjectInterface +{ +} + +class CustomValueObject implements CustomValueObjectInterface +{ + public $value; + + public function __construct($value) + { + $this->value = $value; + } +} + +class DefaultMatchersTest extends MockeryTestCase +{ + public function mockeryTestSetUp() + { + parent::mockeryTestSetUp(); + $this->mock = mock('foo'); + } + + + public function mockeryTestTearDown() + { + \Mockery::getConfiguration()->allowMockingNonExistentMethods(true); + parent::mockeryTestTearDown(); + } + + /** Just a quickie roundup of a few Hamcrest matchers to check nothing obvious out of place **/ + + public function testDefaultMatcherHamcrest() + { + \Mockery::getConfiguration()->setDefaultMatcher(\DateTime::class, \Hamcrest\Core\IsEqual::class); + $this->mock->shouldReceive('foo')->with(new DateTime("2000-01-01"))->once(); + $this->mock->foo(new DateTime("2000-01-01")); + } + + public function testDefaultMatcherClass() + { + \Mockery::getConfiguration()->setDefaultMatcher(CustomValueObject::class, CustomValueObjectMatcher::class); + $this->mock->shouldReceive('foo')->with(new CustomValueObject("expected"))->once(); + $this->mock->foo(new CustomValueObject("expected")); + } + + public function testDefaultMatcherInterface() + { + \Mockery::getConfiguration()->setDefaultMatcher(CustomValueObjectInterface::class, CustomValueObjectMatcher::class); + $this->mock->shouldReceive('foo')->with(new CustomValueObject("expected2"))->once(); + $this->mock->foo(new CustomValueObject("expected2")); + } +}