Skip to content

Commit

Permalink
Merge 8524260 into 3c90be8
Browse files Browse the repository at this point in the history
  • Loading branch information
davedevelopment committed Jan 30, 2017
2 parents 3c90be8 + 8524260 commit a8e5f31
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* `andThrow` will now throw anything `\Throwable`
* Adds `allows` and `expects` syntax
* Adds optional global helpers for `mock`, `namedMock` and `spy`
* Adds ability to create objects using traits

## 0.9.4 (XXXX-XX-XX)

Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,29 @@ $double->shouldHaveReceived()->baz(123); // null
$double->shouldHaveReceived()->baz(12345); // Uncaught Exception Mockery\Exception\InvalidCountException...
```

## Utilities 🔌

### Testing Traits

As Mockery ships with code generation capabilities, it was trivial to add
functionality allowing users to create objects on the fly that use particular
traits. Any abstract methods defined by the trait will be created and can have
expectations or stubs configured like normal Test Doubles.

``` php
trait Foo {
function foo() {
return $this->doFoo();
}

abstract function doFoo();
}

$double = Mockery::mock(Foo::class);
$double->allows()->doFoo()->andReturns(123);
$double->foo(); // int(123)
```

## Documentation

The current version can be seen at [docs.mockery.io](http://docs.mockery.io).
2 changes: 1 addition & 1 deletion library/Mockery/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public function mock()
$builder->setWhiteListedMethods($partialMethods);
array_shift($args);
continue;
} elseif (is_string($arg) && (class_exists($arg, true) || interface_exists($arg, true))) {
} elseif (is_string($arg) && (class_exists($arg, true) || interface_exists($arg, true) || trait_exists($arg, true))) {
$class = array_shift($args);
$builder->addTarget($class);
continue;
Expand Down
43 changes: 43 additions & 0 deletions library/Mockery/Generator/MockConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ class MockConfiguration
protected $targetInterfaces = array();
protected $targetInterfaceNames = array();

/**
* A number of traits we'd like to mock, keyed by name to attempt to
* keep unique
*/
protected $targetTraits = array();
protected $targetTraitNames = array();

/**
* An object we'd like our mock to proxy to
*/
Expand Down Expand Up @@ -115,6 +122,7 @@ public function getHash()
$vars = array(
'targetClassName' => $this->targetClassName,
'targetInterfaceNames' => $this->targetInterfaceNames,
'targetTraitNames' => $this->targetTraitNames,
'name' => $this->name,
'blackListedMethods' => $this->blackListedMethods,
'whiteListedMethod' => $this->whiteListedMethods,
Expand Down Expand Up @@ -224,6 +232,10 @@ public function rename($className)
$targets = array_merge($targets, $this->targetInterfaceNames);
}

if ($this->targetTraitNames) {
$targets = array_merge($targets, $this->targetTraitNames);
}

if ($this->targetObject) {
$targets[] = $this->targetObject;
}
Expand Down Expand Up @@ -261,6 +273,11 @@ protected function addTarget($target)
return $this;
}

if (trait_exists($target)) {
$this->addTargetTraitName($target);
return $this;
}

/**
* Default is to set as class, or interface if class already set
*
Expand Down Expand Up @@ -318,6 +335,20 @@ public function getTargetClass()
return $this->targetClass;
}

public function getTargetTraits()
{
if (!empty($this->targetTraits)) {
return $this->targetTraits;
}

foreach ($this->targetTraitNames as $targetTrait) {
$this->targetTraits[] = DefinedTargetClass::factory($targetTrait);
}

$this->targetTraits = array_unique($this->targetTraits); // just in case
return $this->targetTraits;
}

public function getTargetInterfaces()
{
if (!empty($this->targetInterfaces)) {
Expand Down Expand Up @@ -467,6 +498,14 @@ protected function getAllMethods()
$methods = array_merge($methods, $class->getMethods());
}

foreach ($this->getTargetTraits() AS $trait) {
foreach ($trait->getMethods() as $method) {
if ($method->isAbstract()) {
$methods[] = $method;
}
}
}

$names = array();
$methods = array_filter($methods, function ($method) use (&$names) {
if (in_array($method->getName(), $names)) {
Expand All @@ -490,6 +529,10 @@ protected function addTargetInterfaceName($targetInterface)
$this->targetInterfaceNames[] = $targetInterface;
}

protected function addTargetTraitName($targetTraitName)
{
$this->targetTraitNames[] = $targetTraitName;
}

protected function setTargetObject($object)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,30 @@
* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
*/

namespace Mockery\Generator;
namespace Mockery\Generator\StringManipulation\Pass;

interface TargetClass
{
/** @return string */
public function getName();

/** @return bool */
public function isAbstract();

/** @return bool */
public function isFinal();

/** @return Method[] */
public function getMethods();

/** @return string */
public function getNamespaceName();
use Mockery\Generator\MockConfiguration;

/** @return bool */
public function inNamespace();

/** @return string */
public function getShortName();

/** @return bool */
public function implementsInterface($interface);

/** @return bool */
public function hasInternalAncestor();
class TraitPass implements Pass
{
public function apply($code, MockConfiguration $config)
{
$traits = $config->getTargetTraits();

if (empty($traits)) {
return $code;
}

$useStatements = array_map(function ($trait) {
return "use \\\\".ltrim($trait->getName(), "\\").";";
}, $traits);

$code = preg_replace(
'/^{$/m',
"{\n ".implode("\n ", $useStatements)."\n",
$code
);

return $code;
}
}
2 changes: 2 additions & 0 deletions library/Mockery/Generator/StringManipulationGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Mockery\Generator\StringManipulation\Pass\MagicMethodTypeHintsPass;
use Mockery\Generator\StringManipulation\Pass\ClassNamePass;
use Mockery\Generator\StringManipulation\Pass\ClassPass;
use Mockery\Generator\StringManipulation\Pass\TraitPass;
use Mockery\Generator\StringManipulation\Pass\InstanceMockPass;
use Mockery\Generator\StringManipulation\Pass\InterfacePass;
use Mockery\Generator\StringManipulation\Pass\MethodDefinitionPass;
Expand All @@ -48,6 +49,7 @@ public static function withDefaultPasses()
new CallTypeHintPass(),
new MagicMethodTypeHintsPass(),
new ClassPass(),
new TraitPass(),
new ClassNamePass(),
new InstanceMockPass(),
new InterfacePass(),
Expand Down
1 change: 1 addition & 0 deletions tests/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ function isAbsolutePath($path)

require_once $hamcrestPath;

Mockery::globalHelpers();
/*
* Unset global variables that are no longer needed.
*/
Expand Down
74 changes: 74 additions & 0 deletions tests/Mockery/TraitsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?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/blob/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
* @copyright Copyright (c) 2017 Dave Marshall (dave@atst.io)
* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
*/

namespace test\Mockery;

use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use Mockery\Loader\RequireLoader;

class TraitTest extends \PHPUnit_Framework_TestCase
{
use MockeryPHPUnitIntegration;

/** @test */
public function it_can_create_an_object_for_a_simple_trait()
{
$trait = mock(SimpleTrait::class);

$this->assertEquals('bar', $trait->foo());
}

/** @test */
public function it_creates_abstract_methods_as_necessary()
{
$trait = mock(TraitWithAbstractMethod::class, ['doBaz' => 'baz']);

$this->assertEquals('baz', $trait->baz());
}

/** @test */
public function it_can_create_an_object_using_multiple_traits()
{
$trait = mock(SimpleTrait::class, TraitWithAbstractMethod::class, [
'doBaz' => 123,
]);

$this->assertEquals('bar', $trait->foo());
$this->assertEquals(123, $trait->baz());
}
}

trait SimpleTrait
{
function foo()
{
return 'bar';
}
}

trait TraitWithAbstractMethod
{
function baz()
{
return $this->doBaz();
}

abstract function doBaz();
}

0 comments on commit a8e5f31

Please sign in to comment.