Skip to content

Commit 32fd8dc

Browse files
authored
Merge pull request #37 from facile-it/feature/proxy-generator
Implement a Proxy that extends original mock object
2 parents 7ee2142 + 731b742 commit 32fd8dc

File tree

71 files changed

+2000
-719
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2000
-719
lines changed

README.md

+77-64
Original file line numberDiff line numberDiff line change
@@ -8,75 +8,72 @@
88
[![Packagist](https://img.shields.io/packagist/dt/facile-it/moka.svg)](https://packagist.org/packages/facile-it/moka)
99

1010
Tired of spending most of your testing time mocking objects like there's no tomorrow? **Yes.**
11-
**Moka** provides you with three simple methods to reduce your effort on such a tedious task, and with an incredible abstraction layer between the most popular mock engines and **you**.
11+
**Moka** provides you with two simple methods to reduce your effort on such a tedious task, and with an incredible abstraction layer between the most popular mock engines and **you**.
1212

1313
## Installation
1414

15-
You can simply install the package via composer:
15+
You can install the package via composer:
1616

1717
```bash
1818
composer require --dev facile-it/moka
1919
```
2020

2121
## Usage
2222

23-
To use **Moka** in your tests simply `use` the `MokaPHPUnitTrait` (see generators section [below](#strategies)) and run `Moka::clean()` before every test. A simple interface will let you create *moka* (mock) objects, *serve* them easily, and decorate them with *stub* methods with a fluent interface.
24-
25-
A complete example follows:
23+
To use **Moka** in your tests simply `use` function `Moka\Plugin\PHPUnit\moka()` (see generators section [below](#strategies)) and run `Moka::clean()` before every test. A simple interface will let you create *moka* (mock) objects and decorate them with *stub* methods and properties via a fluent interface:
2624

2725
```php
2826
<?php
2927

3028
namespace Foo\Tests;
3129

3230
use Moka\Moka;
33-
use Moka\Traits\MokaPHPUnitTrait;
31+
use function Moka\Plugin\PHPUnit\moka;
3432

3533
class FooTest extends \AnyTestCase
3634
{
37-
use MokaPHPUnitTrait;
38-
3935
private $foo;
4036

41-
public function setUp()
37+
protected function setUp()
4238
{
4339
Moka::clean();
4440

4541
// The subject of the test.
4642
$this->foo = new Foo(
47-
$this->moka(BarInterface::class)->stub([
43+
moka(BarInterface::class)->stub([
44+
// Property name => value.
45+
'$property' => 3,
4846
// Method name => return value.
49-
'method1' => $this->moka(AcmeInterface::class)->serve(),
50-
'method2' => true // Any return value.
51-
])->serve()
47+
'method1' => moka(AcmeInterface::class),
48+
'method2' => true
49+
])
5250
);
5351
}
5452

5553
//...
5654
}
5755
```
5856

59-
Alternatively, instead of using the trait and `$this->moka(/* ... */)`, you can call `Moka::phpunit(string $fqcnOrAlias, string $alias = null): ProxyInterface`.
57+
Alternatively, instead of using `moka()`, you can call `Moka::phpunit(string $fqcnOrAlias, string $alias = null): ProxyInterface`.
6058

6159
Being such a simple project, **Moka** can be integrated in an already existing test suite with no effort.
6260

63-
**Notice:** if you are extending PHPUnit `TestCase`, to simplify the cleaning phase we provide a `MokaCleanerTrait` which automatically runs `Moka::clean()` in `tearDown()`.
64-
**Warning:** if you are defining your own `tearDown()`, you cannot use the trait!
61+
**Notice:** if you are extending PHPUnit `TestCase`, to simplify the cleaning phase we provide a `MokaCleanerTrait` which automatically runs `Moka::clean()` after each test.
6562

6663
```php
6764
<?php
6865

6966
namespace Foo\Tests;
7067

7168
use Moka\Traits\MokaCleanerTrait;
72-
use Moka\Traits\MokaPHPUnitTrait;
7369
use PHPUnit\Framework\TestCase;
70+
use function Moka\Plugin\PHPUnit\moka;
7471

7572
class FooTest extends TestCase
7673
{
77-
use MokaPHPUnitTrait, MokaCleanerTrait;
74+
use MokaCleanerTrait;
7875

79-
public function setUp()
76+
protected function setUp()
8077
{
8178
// No call to Moka::clean() needed.
8279

@@ -87,23 +84,23 @@ class FooTest extends TestCase
8784
}
8885
```
8986

90-
You can rely on the original mock object implementation to be accessible (in the example below, PHPUnit's):
87+
<a name='original-mock'></a>You can rely on the original mock object implementation to be accessible (in the example below, PHPUnit's - for Prophecy <a href='#prophecy-mock'>see below</a>):
9188

9289
```php
93-
$this->moka(BarInterface::class, 'bar')
90+
moka(BarInterface::class, 'bar')
9491
->expects($this->at(0))
9592
->method('isValid')
9693
->willReturn(true);
9794

98-
$this->moka('bar')
95+
moka('bar')
9996
->expects($this->at(1))
10097
->method('isValid')
10198
->willThrowException(new \Exception());
10299

103-
var_dump($this->moka('bar')->serve()->isValid());
100+
var_dump(moka('bar')->isValid());
104101
// bool(true)
105102

106-
var_dump($this->moka('bar')->serve()->isValid());
103+
var_dump(moka('bar')->isValid());
107104
// throws \Exception
108105
```
109106

@@ -114,8 +111,8 @@ var_dump($this->moka('bar')->serve()->isValid());
114111
Creates a proxy containing a mock object (according to the selected strategy) for the provided *FQCN* and optionally assigns an `$alias` to it to be able to get it later:
115112

116113
```php
117-
$mock1 = $this->moka(FooInterface::class)->serve(); // Creates the mock for FooInterface.
118-
$mock2 = $this->moka(FooInterface::class)->serve(); // Gets a different mock.
114+
$mock1 = moka(FooInterface::class); // Creates the mock for FooInterface.
115+
$mock2 = moka(FooInterface::class); // Gets a different mock.
119116

120117
var_dump($mock1 === $mock2);
121118
// bool(false)
@@ -124,46 +121,38 @@ var_dump($mock1 === $mock2);
124121
The `$alias` allows you to store mock instances:
125122

126123
```php
127-
$mock1 = $this->moka(FooInterface::class, 'foo')->serve(); // Creates a mock for FooInterface.
128-
$mock2 = $this->moka('foo')->serve(); // Get the mock previously created.
124+
$mock1 = moka(FooInterface::class, 'foo'); // Creates a mock for FooInterface.
125+
$mock2 = moka('foo'); // Get the mock previously created.
129126

130127
var_dump($mock1 === $mock2);
131128
// bool(true)
132129
```
133130

134-
### `stub(array $methodsWithValues): ProxyInterface`
131+
### `ProxyInterface::stub(array $namesWithValues): ProxyInterface`
132+
133+
Accepts an array of method or property stubs with format `[$name => $value]`, where `$name` **must** be a string and `$value` can be of any type, including another mock object.
135134

136-
Accepts an array of method stubs with format `[$methodName => $methodValue]`, where `$methodName` **must** be a string and `$methodValue` can be of any type, including another mock object or an exception instance:
135+
**Caution**:
136+
- Properties are identified by symbol `$` prepended to their names
137+
- An exception instance set as a method value will be thrown when the method is called
137138

138139
```php
139-
$actualMock = $this->moka(BarInterface::class)->stub([
140+
$mock = moka(BarInterface::class)->stub([
141+
'$property' => 1,
140142
'isValid' => true,
141-
// Remember to use serve() to pass the actual mock.
142-
'getMock' => $this->moka(AcmeInterface::class)->serve(),
143+
'getMock' => moka(AcmeInterface::class),
143144
'throwException' => new \Exception()
144-
])->serve();
145+
]);
145146

146-
var_dump($actualMock->isValid());
147+
var_dump($mock->property);
148+
// int(1)
149+
150+
var_dump($mock->isValid());
147151
// bool(true)
148152
```
149153

150-
**Notice:** the stub is valid for **any** invocation of the method and it cannot be overridden.
151-
If you need more granular control over invocation strategies, see `serve()`.
152-
153-
### `serve() // Returns the actual mocked object.`
154-
155-
Returns the final *fake* object good to be used in place of the real implementation:
156-
157-
```php
158-
function foo(BarInterface $bar)
159-
{
160-
return $bar->chill();
161-
}
162-
163-
$chill = foo(
164-
$this->moka(BarInterface::class)->serve()
165-
);
166-
```
154+
**Notice:** method stubs are valid for **any** invocation of the defined methods and cannot be overridden.
155+
If you need more granular control over invocation strategies, you can get [access to the original mock object implementation](#original-mock).
167156

168157
## <a name='strategies'></a>Supported mock object generators
169158

@@ -174,14 +163,28 @@ We support other generators as well, but you need to install the relevant packag
174163
- [Mockery](http://docs.mockery.io/en/latest/) -> [mockery/mockery](https://packagist.org/packages/mockery/mockery)
175164
- [Phake](http://phake.readthedocs.io/) -> [phake/phake](https://packagist.org/packages/phake/phake)
176165

177-
We provide a specific trait for each supported strategy, as well as a static method (self documented in the trait itself):
166+
We provide a specific `moka()` function for each supported strategy, as well as a static method (self documented in the function itself):
178167

179-
- `MokaPHPUnitTrait`
180-
- `MokaProphecyTrait`
181-
- `MokaMockeryTrait`
182-
- `MokaPhakeTrait`
168+
- `Moka\Plugin\PHPUnit\moka`
169+
- `Moka\Plugin\Prophecy\moka`
170+
- `Moka\Plugin\Mockery\moka`
171+
- `Moka\Plugin\Phake\moka`
183172

184-
Every trait defines its own `moka(string $fqcnOrAlias, string $alias = null): ProxyInterface`, as described in the **[Reference](#reference)**.
173+
### <a name='prophecy-mock'></a>Prophecy native behavior
174+
175+
Prophecy lets you stub methods by calling them directly on the `ObjectProphecy`. **Moka** doesn't support such a behavior, but we provide an easy workaround:
176+
177+
```php
178+
// Native Prophecy behavior...
179+
$this->prophesize(FooInterface::class)
180+
->someMethod(new AnyValuesToken())
181+
->willReturn($something);
182+
183+
// ...translates to...
184+
Moka::prophecy(FooInterface::class)
185+
->someMethod->set(new AnyValuesToken())
186+
->willReturn($something);
187+
```
185188

186189
## Plugin development
187190

@@ -212,25 +215,35 @@ Extend `AbstractMockingStrategy` for an easier (and stricter) implementation of
212215
namespace Moka\Plugin\YourOwn;
213216

214217
use Moka\Strategy\AbstractMockingStrategy;
215-
use Moka\Stub\Stub;
218+
use Moka\Stub\MethodStub;
216219

217220
class YourOwnMockingStrategy extends AbstractMockingStrategy
218221
{
219-
public function __construct() {
222+
public function __construct()
223+
{
220224
// TODO: Implement __construct() method.
221225
}
222226

223-
protected function doBuild(string $fqcn) {
227+
protected function doBuild(string $fqcn)
228+
{
224229
// TODO: Implement doBuild() method.
225230
}
226231

227-
protected function doDecorate($mock, Stub $stub) {
228-
// TODO: Implement doDecorate() method.
232+
protected function doDecorateWithMethod($mock, MethodStub $stub)
233+
{
234+
// TODO: Implement doDecorateWithMethod() method.
229235
}
230236

231-
protected function doGet($mock) {
237+
protected function doGet($mock)
238+
{
232239
// TODO: Implement doGet() method.
233240
}
241+
242+
protected function doCall($mock, string $methodName)
243+
{
244+
// Override doCall() if you need special behavior.
245+
// See ProphecyMockingStrategy::doCall().
246+
}
234247
}
235248
```
236249

composer.json

+11-7
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@
3131
},
3232
"files": [
3333
"src/Moka/Plugin/Mockery/MockeryPlugin.php",
34+
"src/Moka/Plugin/Mockery/moka.php",
3435
"src/Moka/Plugin/Phake/PhakePlugin.php",
36+
"src/Moka/Plugin/Phake/moka.php",
3537
"src/Moka/Plugin/PHPUnit/PHPUnitPlugin.php",
36-
"src/Moka/Plugin/Prophecy/ProphecyPlugin.php"
38+
"src/Moka/Plugin/PHPUnit/moka.php",
39+
"src/Moka/Plugin/Prophecy/ProphecyPlugin.php",
40+
"src/Moka/Plugin/Prophecy/moka.php"
3741
]
3842
},
3943
"autoload-dev": {
@@ -43,16 +47,16 @@
4347
},
4448
"require": {
4549
"php": ">=7.0",
46-
"phpcollection/phpcollection": "^0.5.0",
50+
"phpcollection/phpcollection": "^0.5",
4751
"psr/container": "^1.0",
48-
"phpunit/phpunit-mock-objects": "^3.4||^4.0"
52+
"phpunit/phpunit-mock-objects": "^4.0"
4953
},
5054
"require-dev": {
5155
"friendsofphp/php-cs-fixer": "^2.3",
52-
"phpunit/phpunit": "^5.6||^6.0",
53-
"phpspec/prophecy": "^1.6",
54-
"mockery/mockery": "^0.9.9",
55-
"phake/phake": "^2.3"
56+
"phpunit/phpunit": "^6.0",
57+
"phpspec/prophecy": "^1.7",
58+
"phake/phake": "^3.0",
59+
"mockery/mockery": "^0.9.9"
5660
},
5761
"suggest": {
5862
"phpspec/prophecy": "To allow loading of ProphecyPlugin",

phpunit.xml.dist

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<directory>src</directory>
2626
<exclude>
2727
<directory>src/Moka/Exception</directory>
28+
<directory suffix="moka.php">src/Moka/Plugin/*</directory>
2829
<directory>src/Moka/Tests</directory>
2930
<directory>src/Moka/Traits</directory>
3031
</exclude>

src/Moka/Builder/ProxyBuilder.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
namespace Moka\Builder;
55

6+
use Moka\Exception\InvalidArgumentException;
67
use Moka\Exception\InvalidIdentifierException;
78
use Moka\Exception\MockNotCreatedException;
8-
use Moka\Factory\ProxyFactory;
9+
use Moka\Factory\ProxyGeneratorFactory;
910
use Moka\Proxy\ProxyContainer;
1011
use Moka\Proxy\ProxyInterface;
1112
use Moka\Strategy\MockingStrategyInterface;
@@ -72,9 +73,12 @@ public function getProxy(string $fqcnOrAlias, string $alias = null): ProxyInterf
7273
/**
7374
* @param string $fqcn
7475
* @return ProxyInterface
76+
*
77+
* @throws InvalidArgumentException
78+
* @throws MockNotCreatedException
7579
*/
7680
protected function buildProxy(string $fqcn): ProxyInterface
7781
{
78-
return ProxyFactory::get($fqcn, $this->mockingStrategy);
82+
return ProxyGeneratorFactory::get($this->mockingStrategy)->get($fqcn);
7983
}
8084
}

src/Moka/Factory/ProxyFactory.php

-35
This file was deleted.

0 commit comments

Comments
 (0)