Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable default matchers #1120

Merged
merged 1 commit into from
Jul 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions library/Mockery/Configuration.php
Expand Up @@ -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
*
Expand Down Expand Up @@ -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;
}
}
6 changes: 6 additions & 0 deletions library/Mockery/Expectation.php
Expand Up @@ -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);
}
Expand Down
88 changes: 88 additions & 0 deletions tests/Mockery/DefaultMatchersTest.php
@@ -0,0 +1,88 @@
<?php
/**
* Mockery
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://github.com/padraic/mockery/master/LICENSE
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to padraic@php.net so we can send you a copy immediately.
*
* @category Mockery
* @package Mockery
* @subpackage UnitTests
* @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
*/

use Mockery\Adapter\Phpunit\MockeryTestCase;

class CustomValueObjectMatcher extends \Mockery\Matcher\MatcherAbstract
{
public function match(&$actual)
{
return $actual->value === $this->_expected->value;
}

public function __toString()
{
return "<customMatcher>";
}
}

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"));
}
}