This commit is contained in:
2020-10-06 14:27:47 +07:00
commit 586be80cf6
16613 changed files with 3274099 additions and 0 deletions

3
vendor/codeception/stub/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea/
/vendor/
composer.lock

21
vendor/codeception/stub/.travis.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7
- 7.1
- 7.2
cache:
directories:
- vendor
- $HOME/.composer/cache
sudo: false
before_install:
- composer update --prefer-source
script: vendor/bin/phpunit tests

89
vendor/codeception/stub/Readme.md vendored Normal file
View File

@@ -0,0 +1,89 @@
# Codeception\Stub
[![Build Status](https://travis-ci.org/Codeception/Stub.svg?branch=master)](https://travis-ci.org/Codeception/Stub)
[![Latest Stable Version](https://poser.pugx.org/codeception/stub/v/stable)](https://packagist.org/packages/codeception/stub)
[![Total Downloads](https://poser.pugx.org/codeception/stub/downloads)](https://packagist.org/packages/codeception/stub)
[![License](https://poser.pugx.org/codeception/stub/license)](https://packagist.org/packages/codeception/stub)
Library on top of PHPUnit's mock builder providing a highly simplified syntax:
## Reference
* [Stub](https://github.com/Codeception/Stub/blob/master/docs/Stub.md) - creating stub classes using static methods
* [Stub Trait](https://github.com/Codeception/Stub/blob/master/docs/StubTrait.md) - creating stubs and mocks using trait
* [Expected](https://github.com/Codeception/Stub/blob/master/docs/Expected.md) - defining expectations for mocks
## Install
Enabled by default in Codeception.
For PHPUnit install this package:
```
composer require codeception/stub --dev
```
## Stubs
Stubs can be constructed with `Codeception\Stub` static calls:
```php
<?php
// create a stub with find method replaced
$userRepository = Stub::make(UserRepository::class, ['find' => new User]);
$userRepository->find(1); // => User
// create a dummy
$userRepository = Stub::makeEmpty(UserRepository::class);
// create a stub with all methods replaced except one
$user = Stub::makeEmptyExcept(User::class, 'validate');
$user->validate($data);
// create a stub by calling constructor and replacing a method
$user = Stub::construct(User::class, ['name' => 'davert'], ['save' => false]);
// create a stub by calling constructor with empty methods
$user = Stub::constructEmpty(User::class, ['name' => 'davert']);
// create a stub by calling constructor with empty methods
$user = Stub::constructEmptyExcept(User::class, 'getName', ['name' => 'davert']);
$user->getName(); // => davert
$user->setName('jane'); // => this method is empty
$user->getName(); // => davert
```
[See complete reference](https://github.com/Codeception/Stub/blob/master/docs/Stub.md)
Alternatively, stubs can be created by using [`Codeception\Test\Feature\Stub` trait](https://github.com/Codeception/Stub/blob/master/docs/StubTrait.md):
```php
<?php
$this->make(UserRepositry::class);
$this->makeEmpty(UserRepositry::class);
$this->construct(UserRepositry::class);
$this->constructEmpty(UserRepositry::class);
// ...
```
## Mocks
Mocks should be created by including [`Codeception\Test\Feature\Stub` trait](https://github.com/Codeception/Stub/blob/master/docs/StubTrait.md) into a test case.
Execution expectation are set with [`Codescption\Stub\Expected`](https://github.com/Codeception/Stub/blob/master/docs/Expected.md):
```php
<?php
// find should be never called
$userRepository = $this->make(UserRepository::class, [
'find' => Codeception\Stub\Expected::never()
]);
// find should be called once and return a new user
$userRepository = $this->make(UserRepository::class, [
'find' => Codeception\Stub\Expected::once(new User)
]);
```
## License
MIT

42
vendor/codeception/stub/RoboFile.php vendored Normal file
View File

@@ -0,0 +1,42 @@
<?php
require_once 'vendor/autoload.php';
/**
* This is project's console commands configuration for Robo task runner.
*
* @see http://robo.li/
*/
class RoboFile extends \Robo\Tasks
{
protected $docs = [
'docs/Stub.md' => 'Codeception\Stub',
'docs/Expected.md' => 'Codeception\Stub\Expected',
'docs/StubTrait.md' => 'Codeception\Test\Feature\Stub',
];
public function docs()
{
foreach ($this->docs as $file => $class) {
if (!class_exists($class, true) && !trait_exists($class, true)) {
throw new Exception('ups');
}
$this->say("Here goes, $class");
$this->taskGenDoc($file)
->docClass($class)
->filterMethods(function(\ReflectionMethod $method) {
if ($method->isConstructor() or $method->isDestructor()) return false;
if (!$method->isPublic()) return false;
if (strpos($method->name, '_') === 0) return false;
if (strpos($method->name, 'stub') === 0) return false;
return true;
})
->processMethodDocBlock(
function (\ReflectionMethod $m, $doc) {
$doc = str_replace(array('@since'), array(' * available since version'), $doc);
$doc = str_replace(array(' @', "\n@"), array(" * ", "\n * "), $doc);
return $doc;
})
->processProperty(false)
->run();
}
}
}

15
vendor/codeception/stub/composer.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "codeception/stub",
"description":"Flexible Stub wrapper for PHPUnit's Mock Builder",
"type": "library",
"license":"MIT",
"minimum-stability": "stable",
"autoload": {
"psr-4": {
"Codeception\\": "src/"
}
},
"require": {
"phpunit/phpunit": ">=4.8 <8.0"
}
}

132
vendor/codeception/stub/docs/Expected.md vendored Normal file
View File

@@ -0,0 +1,132 @@
## Codeception\Stub\Expected
#### *public static* never($params = null)
Checks if a method never has been invoked
If method invoked, it will immediately throw an
exception.
```php
<?php
use \Codeception\Stub\Expected;
$user = $this->make('User', [
'getName' => Expected::never(),
'someMethod' => function() {}
]);
$user->someMethod();
?>
```
* `param mixed` $params
* return StubMarshaler
#### *public static* once($params = null)
Checks if a method has been invoked exactly one
time.
If the number is less or greater it will later be checked in verify() and also throw an
exception.
```php
<?php
use \Codeception\Stub\Expected;
$user = $this->make(
'User',
array(
'getName' => Expected::once('Davert'),
'someMethod' => function() {}
)
);
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
?>
```
Alternatively, a function can be passed as parameter:
```php
<?php
Expected::once(function() { return Faker::name(); });
```
* `param mixed` $params
* return StubMarshaler
#### *public static* atLeastOnce($params = null)
Checks if a method has been invoked at least one
time.
If the number of invocations is 0 it will throw an exception in verify.
```php
<?php
use \Codeception\Stub\Expected;
$user = $this->make(
'User',
array(
'getName' => Expected::atLeastOnce('Davert')),
'someMethod' => function() {}
)
);
$user->getName();
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
?>
```
Alternatively, a function can be passed as parameter:
```php
<?php
Expected::atLeastOnce(function() { return Faker::name(); });
```
* `param mixed` $params
* return StubMarshaler
#### *public static* exactly($count, $params = null)
Checks if a method has been invoked a certain amount
of times.
If the number of invocations exceeds the value it will immediately throw an
exception,
If the number is less it will later be checked in verify() and also throw an
exception.
``` php
<?php
use \Codeception\Stub;
use \Codeception\Stub\Expected;
$user = $this->make(
'User',
array(
'getName' => Expected::exactly(3, 'Davert'),
'someMethod' => function() {}
)
);
$user->getName();
$user->getName();
$userName = $user->getName();
$this->assertEquals('Davert', $userName);
?>
```
Alternatively, a function can be passed as parameter:
```php
<?php
Expected::exactly(function() { return Faker::name() });
```
* `param int` $count
* `param mixed` $params
* return StubMarshaler

328
vendor/codeception/stub/docs/Stub.md vendored Normal file
View File

@@ -0,0 +1,328 @@
## Codeception\Stub
#### *public static* make($class, $params = null, $testCase = null)
Instantiates a class without executing a constructor.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
Stub::make('User');
Stub::make('User', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
Stub::make(new User, ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::make('User', ['save' => function () { return true; }]);
Stub::make('User', ['save' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::make('User', [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class - A class to be mocked
* `param array` $params - properties and methods to set
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object - mock
* throws \RuntimeException when class does not exist
* throws \Exception
#### *public static* factory($class, $num = null, $params = null)
Creates $num instances of class through `Stub::make`.
* `param mixed` $class
* `param int` $num
* `param array` $params
* return array
* throws \Exception
#### *public static* makeEmptyExcept($class, $method, $params = null, $testCase = null)
Instantiates class having all methods replaced with dummies except one.
Constructor is not triggered.
Properties and methods can be replaced.
Even protected and private properties can be set.
``` php
<?php
Stub::makeEmptyExcept('User', 'save');
Stub::makeEmptyExcept('User', 'save', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
* Stub::makeEmptyExcept(new User, 'save');
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
Stub::makeEmptyExcept('User', 'save', ['isValid' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::makeEmptyExcept('User', 'validate', [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param string` $method
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public static* makeEmpty($class, $params = null, $testCase = null)
Instantiates class having all methods replaced with dummies.
Constructor is not triggered.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
Stub::makeEmpty('User');
Stub::makeEmpty('User', ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
Stub::makeEmpty(new User, ['name' => 'davert']);
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::makeEmpty('User', ['save' => function () { return true; }]);
Stub::makeEmpty('User', ['save' => true));
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::makeEmpty('User', [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public static* copy($obj, $params = null)
Clones an object and redefines it's properties (even protected and private)
* `param` $obj
* `param array` $params
* return mixed
* throws \Exception
#### *public static* construct($class, $constructorParams = null, $params = null, $testCase = null)
Instantiates a class instance by running constructor.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
Stub::construct('User', ['autosave' => false]);
Stub::construct('User', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
Stub::construct(new User, ['autosave' => false), ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::construct('User', [], ['save' => function () { return true; }]);
Stub::construct('User', [], ['save' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::construct('User', [], [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public static* constructEmpty($class, $constructorParams = null, $params = null, $testCase = null)
Instantiates a class instance by running constructor with all methods replaced with dummies.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
Stub::constructEmpty('User', ['autosave' => false]);
Stub::constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
Stub::constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::constructEmpty('User', [], ['save' => function () { return true; }]);
Stub::constructEmpty('User', [], ['save' => true]);
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::constructEmpty('User', [], [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
#### *public static* constructEmptyExcept($class, $method, $constructorParams = null, $params = null, $testCase = null)
Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
Stub::constructEmptyExcept('User', 'save');
Stub::constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
Stub::constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
Stub::constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
Stub::constructEmptyExcept('User', 'save', [], ['save' => true]);
?>
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
Stub::constructEmptyExcept('User', 'save', [], [
'save' => \Codeception\Stub\Expected::once()
], $this);
```
* `param mixed` $class
* `param string` $method
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
#### *public static* update($mock, array $params)
Replaces properties of current stub
* `param \PHPUnit\Framework\MockObject\MockObject` $mock
* `param array` $params
* return mixed
* throws \LogicException
#### *public static* consecutive()
Stubbing a method call to return a list of values in the specified order.
``` php
<?php
$user = Stub::make('User', array('getName' => Stub::consecutive('david', 'emma', 'sam', 'amy')));
$user->getName(); //david
$user->getName(); //emma
$user->getName(); //sam
$user->getName(); //amy
?>
```
* return ConsecutiveMap

View File

@@ -0,0 +1,250 @@
## Codeception\Test\Feature\Stub
### Usage in Codeception
Since Codeception 2.3.8 this trait is enabled in `\Codeception\Test\Unit` class.
### Usage in PHPUnit
Include this trait into a TestCase to be able to use Stubs and Mocks:
```php
<?php
class MyTest extends \PHPUnit\Framework\TestCase
{
use Codeception\Test\Feature\Stub;
}
```
#### *public* make($class, $params = null)
Instantiates a class without executing a constructor.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
$this->make('User');
$this->make('User', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
$this->make(new User, ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
$this->make('User', ['save' => function () { return true; }]);
$this->make('User', ['save' => true]);
```
* `param mixed` $class - A class to be mocked
* `param array` $params - properties and methods to set
* return object - mock
* throws \RuntimeException when class does not exist
* throws \Exception
#### *public* makeEmpty($class, $params = null)
Instantiates class having all methods replaced with dummies.
Constructor is not triggered.
Properties and methods can be set as a second parameter.
Even protected and private properties can be set.
``` php
<?php
$this->makeEmpty('User');
$this->makeEmpty('User', ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
$this->makeEmpty(new User, ['name' => 'davert']);
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
$this->makeEmpty('User', ['save' => function () { return true; }]);
$this->makeEmpty('User', ['save' => true));
```
* `param mixed` $class
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public* makeEmptyExcept($class, $method, $params = null)
Instantiates class having all methods replaced with dummies except one.
Constructor is not triggered.
Properties and methods can be replaced.
Even protected and private properties can be set.
``` php
<?php
$this->makeEmptyExcept('User', 'save');
$this->makeEmptyExcept('User', 'save', ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
* $this->makeEmptyExcept(new User, 'save');
?>
```
To replace method provide it's name as a key in second parameter
and it's return value or callback function as parameter
``` php
<?php
$this->makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
$this->makeEmptyExcept('User', 'save', ['isValid' => true]);
```
* `param mixed` $class
* `param string` $method
* `param array` $params
* return object
* throws \Exception
#### *public* construct($class, $constructorParams = null, $params = null)
Instantiates a class instance by running constructor.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
$this->construct('User', ['autosave' => false]);
$this->construct('User', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
$this->construct(new User, ['autosave' => false), ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
$this->construct('User', [], ['save' => function () { return true; }]);
$this->construct('User', [], ['save' => true]);
?>
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* `param bool|\PHPUnit\Framework\TestCase` $testCase
* return object
* throws \Exception
#### *public* constructEmpty($class, $constructorParams = null, $params = null)
Instantiates a class instance by running constructor with all methods replaced with dummies.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
$this->constructEmpty('User', ['autosave' => false]);
$this->constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
```
Accepts either name of class or object of that class
``` php
<?php
$this->constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
$this->constructEmpty('User', array(), array('save' => function () { return true; }));
$this->constructEmpty('User', array(), array('save' => true));
```
**To create a mock, pass current testcase name as last argument:**
```php
<?php
$this->constructEmpty('User', [], [
'save' => \Codeception\Stub\Expected::once()
]);
```
* `param mixed` $class
* `param array` $constructorParams
* `param array` $params
* return object
#### *public* constructEmptyExcept($class, $method, $constructorParams = null, $params = null)
Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
Parameters for constructor passed as second argument
Properties and methods can be set in third argument.
Even protected and private properties can be set.
``` php
<?php
$this->constructEmptyExcept('User', 'save');
$this->constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
Accepts either name of class or object of that class
``` php
<?php
$this->constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
?>
```
To replace method provide it's name as a key in third parameter
and it's return value or callback function as parameter
``` php
<?php
$this->constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
$this->constructEmptyExcept('User', 'save', [], ['save' => true]);
?>
```
* `param mixed` $class
* `param string` $method
* `param array` $constructorParams
* `param array` $params
* return object

686
vendor/codeception/stub/src/Stub.php vendored Normal file
View File

@@ -0,0 +1,686 @@
<?php
namespace Codeception;
require_once __DIR__ . DIRECTORY_SEPARATOR . 'shim.php';
use Codeception\Stub\ConsecutiveMap;
use Codeception\Stub\StubMarshaler;
use PHPUnit\Framework\MockObject\Generator;
use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
class Stub
{
public static $magicMethods = ['__isset', '__get', '__set'];
/**
* Instantiates a class without executing a constructor.
* Properties and methods can be set as a second parameter.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* Stub::make('User');
* Stub::make('User', ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* Stub::make(new User, ['name' => 'davert']);
* ?>
* ```
*
* To replace method provide it's name as a key in second parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* Stub::make('User', ['save' => function () { return true; }]);
* Stub::make('User', ['save' => true]);
* ?>
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* Stub::make('User', [
* 'save' => \Codeception\Stub\Expected::once()
* ], $this);
* ```
*
* @param mixed $class - A class to be mocked
* @param array $params - properties and methods to set
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object - mock
* @throws \RuntimeException when class does not exist
* @throws \Exception
*/
public static function make($class, $params = [], $testCase = false)
{
$class = self::getClassname($class);
if (!class_exists($class)) {
if (interface_exists($class)) {
throw new \RuntimeException("Stub::make can't mock interfaces, please use Stub::makeEmpty instead.");
}
throw new \RuntimeException("Stubbed class $class doesn't exist.");
}
$reflection = new \ReflectionClass($class);
$callables = self::getMethodsToReplace($reflection, $params);
if ($reflection->isAbstract()) {
$arguments = empty($callables) ? [] : array_keys($callables);
$mock = self::generateMockForAbstractClass($class, $arguments, '', false, $testCase);
} else {
$arguments = empty($callables) ? null : array_keys($callables);
$mock = self::generateMock($class, $arguments, [], '', false, $testCase);
}
self::bindParameters($mock, $params);
return self::markAsMock($mock, $reflection);
}
/**
* Set __mock flag, if at all possible
*
* @param object $mock
* @param \ReflectionClass $reflection
* @return object
*/
private static function markAsMock($mock, \ReflectionClass $reflection)
{
if (!$reflection->hasMethod('__set')) {
$mock->__mocked = $reflection->getName();
}
return $mock;
}
/**
* Creates $num instances of class through `Stub::make`.
*
* @param mixed $class
* @param int $num
* @param array $params
*
* @return array
* @throws \Exception
*/
public static function factory($class, $num = 1, $params = [])
{
$objects = [];
for ($i = 0; $i < $num; $i++) {
$objects[] = self::make($class, $params);
}
return $objects;
}
/**
* Instantiates class having all methods replaced with dummies except one.
* Constructor is not triggered.
* Properties and methods can be replaced.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* Stub::makeEmptyExcept('User', 'save');
* Stub::makeEmptyExcept('User', 'save', ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* * Stub::makeEmptyExcept(new User, 'save');
* ?>
* ```
*
* To replace method provide it's name as a key in second parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* Stub::makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
* Stub::makeEmptyExcept('User', 'save', ['isValid' => true]);
* ?>
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* Stub::makeEmptyExcept('User', 'validate', [
* 'save' => \Codeception\Stub\Expected::once()
* ], $this);
* ```
*
* @param mixed $class
* @param string $method
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
* @throws \Exception
*/
public static function makeEmptyExcept($class, $method, $params = [], $testCase = false)
{
$class = self::getClassname($class);
$reflectionClass = new \ReflectionClass($class);
$methods = $reflectionClass->getMethods();
$methods = array_filter(
$methods,
function ($m) {
return !in_array($m->name, Stub::$magicMethods);
}
);
$methods = array_filter(
$methods,
function ($m) use ($method) {
return $method != $m->name;
}
);
$methods = array_map(
function ($m) {
return $m->name;
},
$methods
);
$methods = count($methods) ? $methods : null;
$mock = self::generateMock($class, $methods, [], '', false, $testCase);
self::bindParameters($mock, $params);
return self::markAsMock($mock, $reflectionClass);
}
/**
* Instantiates class having all methods replaced with dummies.
* Constructor is not triggered.
* Properties and methods can be set as a second parameter.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* Stub::makeEmpty('User');
* Stub::makeEmpty('User', ['name' => 'davert']);
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* Stub::makeEmpty(new User, ['name' => 'davert']);
* ```
*
* To replace method provide it's name as a key in second parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* Stub::makeEmpty('User', ['save' => function () { return true; }]);
* Stub::makeEmpty('User', ['save' => true));
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* Stub::makeEmpty('User', [
* 'save' => \Codeception\Stub\Expected::once()
* ], $this);
* ```
*
* @param mixed $class
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
* @throws \Exception
*/
public static function makeEmpty($class, $params = [], $testCase = false)
{
$class = self::getClassname($class);
$reflection = new \ReflectionClass($class);
$methods = get_class_methods($class);
$methods = array_filter(
$methods,
function ($i) {
return !in_array($i, Stub::$magicMethods);
}
);
$mock = self::generateMock($class, $methods, [], '', false, $testCase);
self::bindParameters($mock, $params);
return self::markAsMock($mock, $reflection);
}
/**
* Clones an object and redefines it's properties (even protected and private)
*
* @param $obj
* @param array $params
*
* @return mixed
* @throws \Exception
*/
public static function copy($obj, $params = [])
{
$copy = clone($obj);
self::bindParameters($copy, $params);
return $copy;
}
/**
* Instantiates a class instance by running constructor.
* Parameters for constructor passed as second argument
* Properties and methods can be set in third argument.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* Stub::construct('User', ['autosave' => false]);
* Stub::construct('User', ['autosave' => false], ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* Stub::construct(new User, ['autosave' => false), ['name' => 'davert']);
* ?>
* ```
*
* To replace method provide it's name as a key in third parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* Stub::construct('User', [], ['save' => function () { return true; }]);
* Stub::construct('User', [], ['save' => true]);
* ?>
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* Stub::construct('User', [], [
* 'save' => \Codeception\Stub\Expected::once()
* ], $this);
* ```
*
* @param mixed $class
* @param array $constructorParams
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
* @throws \Exception
*/
public static function construct($class, $constructorParams = [], $params = [], $testCase = false)
{
$class = self::getClassname($class);
$reflection = new \ReflectionClass($class);
$callables = self::getMethodsToReplace($reflection, $params);
$arguments = empty($callables) ? null : array_keys($callables);
$mock = self::generateMock($class, $arguments, $constructorParams, $testCase);
self::bindParameters($mock, $params);
return self::markAsMock($mock, $reflection);
}
/**
* Instantiates a class instance by running constructor with all methods replaced with dummies.
* Parameters for constructor passed as second argument
* Properties and methods can be set in third argument.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* Stub::constructEmpty('User', ['autosave' => false]);
* Stub::constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* Stub::constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
* ```
*
* To replace method provide it's name as a key in third parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* Stub::constructEmpty('User', [], ['save' => function () { return true; }]);
* Stub::constructEmpty('User', [], ['save' => true]);
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* Stub::constructEmpty('User', [], [
* 'save' => \Codeception\Stub\Expected::once()
* ], $this);
* ```
*
* @param mixed $class
* @param array $constructorParams
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
*/
public static function constructEmpty($class, $constructorParams = [], $params = [], $testCase = false)
{
$class = self::getClassname($class);
$reflection = new \ReflectionClass($class);
$methods = get_class_methods($class);
$methods = array_filter(
$methods,
function ($i) {
return !in_array($i, Stub::$magicMethods);
}
);
$mock = self::generateMock($class, $methods, $constructorParams, $testCase);
self::bindParameters($mock, $params);
return self::markAsMock($mock, $reflection);
}
/**
* Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
* Parameters for constructor passed as second argument
* Properties and methods can be set in third argument.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* Stub::constructEmptyExcept('User', 'save');
* Stub::constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* Stub::constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
* ?>
* ```
*
* To replace method provide it's name as a key in third parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* Stub::constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
* Stub::constructEmptyExcept('User', 'save', [], ['save' => true]);
* ?>
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* Stub::constructEmptyExcept('User', 'save', [], [
* 'save' => \Codeception\Stub\Expected::once()
* ], $this);
* ```
*
* @param mixed $class
* @param string $method
* @param array $constructorParams
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
*/
public static function constructEmptyExcept(
$class,
$method,
$constructorParams = [],
$params = [],
$testCase = false
) {
$class = self::getClassname($class);
$reflectionClass = new \ReflectionClass($class);
$methods = $reflectionClass->getMethods();
$methods = array_filter(
$methods,
function ($m) {
return !in_array($m->name, Stub::$magicMethods);
}
);
$methods = array_filter(
$methods,
function ($m) use ($method) {
return $method != $m->name;
}
);
$methods = array_map(
function ($m) {
return $m->name;
},
$methods
);
$methods = count($methods) ? $methods : null;
$mock = self::generateMock($class, $methods, $constructorParams, $testCase);
self::bindParameters($mock, $params);
return self::markAsMock($mock, $reflectionClass);
}
private static function generateMock()
{
return self::doGenerateMock(func_get_args());
}
/**
* Returns a mock object for the specified abstract class with all abstract
* methods of the class mocked. Concrete methods to mock can be specified with
* the last parameter
*
* @return object
* @since Method available since Release 1.0.0
*/
private static function generateMockForAbstractClass()
{
return self::doGenerateMock(func_get_args(), true);
}
private static function doGenerateMock($args, $isAbstract = false)
{
$testCase = self::extractTestCaseFromArgs($args);
$methodName = $isAbstract ? 'getMockForAbstractClass' : 'getMock';
$generatorClass = new Generator;
// using PHPUnit 5.4 mocks registration
if (version_compare(\PHPUnit\Runner\Version::series(), '5.4', '>=')
&& $testCase instanceof \PHPUnit\Framework\TestCase
) {
$mock = call_user_func_array([$generatorClass, $methodName], $args);
$testCase->registerMockObject($mock);
return $mock;
}
if ($testCase instanceof \PHPUnit\Framework\TestCase) {
$generatorClass = $testCase;
}
return call_user_func_array([$generatorClass, $methodName], $args);
}
private static function extractTestCaseFromArgs(&$args)
{
$argsLength = count($args) - 1;
$testCase = $args[$argsLength];
unset($args[$argsLength]);
return $testCase;
}
/**
* Replaces properties of current stub
*
* @param \PHPUnit\Framework\MockObject\MockObject $mock
* @param array $params
*
* @return mixed
* @throws \LogicException
*/
public static function update($mock, array $params)
{
//do not rely on __mocked property, check typ eof $mock
if (!$mock instanceof \PHPUnit\Framework\MockObject\MockObject) {
throw new \LogicException('You can update only stubbed objects');
}
self::bindParameters($mock, $params);
return $mock;
}
/**
* @param \PHPUnit\Framework\MockObject\MockObject $mock
* @param array $params
* @throws \LogicException
*/
protected static function bindParameters($mock, $params)
{
$reflectionClass = new \ReflectionClass($mock);
if ($mock instanceof \PHPUnit\Framework\MockObject\MockObject) {
$parentClass = $reflectionClass->getParentClass();
if ($parentClass !== false) {
$reflectionClass = $reflectionClass->getParentClass();
}
}
foreach ($params as $param => $value) {
// redefine method
if ($reflectionClass->hasMethod($param)) {
if ($value instanceof StubMarshaler) {
$marshaler = $value;
$mock
->expects($marshaler->getMatcher())
->method($param)
->will(new ReturnCallback($marshaler->getValue()));
} elseif ($value instanceof \Closure) {
$mock
->expects(new AnyInvokedCount)
->method($param)
->will(new ReturnCallback($value));
} elseif ($value instanceof ConsecutiveMap) {
$consecutiveMap = $value;
$mock
->expects(new AnyInvokedCount)
->method($param)
->will(new ConsecutiveCalls($consecutiveMap->getMap()));
} else {
$mock
->expects(new AnyInvokedCount)
->method($param)
->will(new ReturnStub($value));
}
} elseif ($reflectionClass->hasProperty($param)) {
$reflectionProperty = $reflectionClass->getProperty($param);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($mock, $value);
continue;
} else {
if ($reflectionClass->hasMethod('__set')) {
try {
$mock->{$param} = $value;
} catch (\Exception $e) {
throw new \LogicException(
sprintf(
'Could not add property %1$s, class %2$s implements __set method, '
. 'and no %1$s property exists',
$param,
$reflectionClass->getName()
),
$e->getCode(),
$e
);
}
} else {
$mock->{$param} = $value;
}
continue;
}
}
}
/**
* @todo should be simplified
*/
protected static function getClassname($object)
{
if (is_object($object)) {
return get_class($object);
}
if (is_callable($object)) {
return call_user_func($object);
}
return $object;
}
/**
* @param \ReflectionClass $reflection
* @param $params
* @return array
*/
protected static function getMethodsToReplace(\ReflectionClass $reflection, $params)
{
$callables = [];
foreach ($params as $method => $value) {
if ($reflection->hasMethod($method)) {
$callables[$method] = $value;
}
}
return $callables;
}
/**
* Stubbing a method call to return a list of values in the specified order.
*
* ``` php
* <?php
* $user = Stub::make('User', array('getName' => Stub::consecutive('david', 'emma', 'sam', 'amy')));
* $user->getName(); //david
* $user->getName(); //emma
* $user->getName(); //sam
* $user->getName(); //amy
* ?>
* ```
*
* @return ConsecutiveMap
*/
public static function consecutive()
{
return new ConsecutiveMap(func_get_args());
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Codeception\Stub;
/**
* Holds matcher and value of mocked method
*/
class ConsecutiveMap
{
private $consecutiveMap = [];
public function __construct(array $consecutiveMap)
{
$this->consecutiveMap = $consecutiveMap;
}
public function getMap()
{
return $this->consecutiveMap;
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace Codeception\Stub;
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'shim.php';
use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastOnce;
use PHPUnit\Framework\MockObject\Matcher\InvokedCount;
class Expected
{
/**
* Checks if a method never has been invoked
*
* If method invoked, it will immediately throw an
* exception.
*
* ```php
* <?php
* use \Codeception\Stub\Expected;
*
* $user = $this->make('User', [
* 'getName' => Expected::never(),
* 'someMethod' => function() {}
* ]);
* $user->someMethod();
* ?>
* ```
*
* @param mixed $params
* @return StubMarshaler
*/
public static function never($params = null)
{
return new StubMarshaler(
new InvokedCount(0),
self::closureIfNull($params)
);
}
/**
* Checks if a method has been invoked exactly one
* time.
*
* If the number is less or greater it will later be checked in verify() and also throw an
* exception.
*
* ```php
* <?php
* use \Codeception\Stub\Expected;
*
* $user = $this->make(
* 'User',
* array(
* 'getName' => Expected::once('Davert'),
* 'someMethod' => function() {}
* )
* );
* $userName = $user->getName();
* $this->assertEquals('Davert', $userName);
* ?>
* ```
* Alternatively, a function can be passed as parameter:
*
* ```php
* <?php
* Expected::once(function() { return Faker::name(); });
* ```
*
* @param mixed $params
*
* @return StubMarshaler
*/
public static function once($params = null)
{
return new StubMarshaler(
new InvokedCount(1),
self::closureIfNull($params)
);
}
/**
* Checks if a method has been invoked at least one
* time.
*
* If the number of invocations is 0 it will throw an exception in verify.
*
* ```php
* <?php
* use \Codeception\Stub\Expected;
*
* $user = $this->make(
* 'User',
* array(
* 'getName' => Expected::atLeastOnce('Davert')),
* 'someMethod' => function() {}
* )
* );
* $user->getName();
* $userName = $user->getName();
* $this->assertEquals('Davert', $userName);
* ?>
* ```
*
* Alternatively, a function can be passed as parameter:
*
* ```php
* <?php
* Expected::atLeastOnce(function() { return Faker::name(); });
* ```
*
* @param mixed $params
*
* @return StubMarshaler
*/
public static function atLeastOnce($params = null)
{
return new StubMarshaler(
new InvokedAtLeastOnce(),
self::closureIfNull($params)
);
}
/**
* Checks if a method has been invoked a certain amount
* of times.
* If the number of invocations exceeds the value it will immediately throw an
* exception,
* If the number is less it will later be checked in verify() and also throw an
* exception.
*
* ``` php
* <?php
* use \Codeception\Stub;
* use \Codeception\Stub\Expected;
*
* $user = $this->make(
* 'User',
* array(
* 'getName' => Expected::exactly(3, 'Davert'),
* 'someMethod' => function() {}
* )
* );
* $user->getName();
* $user->getName();
* $userName = $user->getName();
* $this->assertEquals('Davert', $userName);
* ?>
* ```
* Alternatively, a function can be passed as parameter:
*
* ```php
* <?php
* Expected::exactly(function() { return Faker::name() });
* ```
*
* @param int $count
* @param mixed $params
*
* @return StubMarshaler
*/
public static function exactly($count, $params = null)
{
return new StubMarshaler(
new InvokedCount($count),
self::closureIfNull($params)
);
}
private static function closureIfNull($params)
{
if ($params instanceof \Closure) {
return $params;
}
return function() use ($params) {
return $params;
};
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Codeception\Stub;
use PHPUnit\Framework\MockObject\Matcher\InvokedRecorder;
/**
* Holds matcher and value of mocked method
*/
class StubMarshaler
{
private $methodMatcher;
private $methodValue;
public function __construct(InvokedRecorder $matcher, $value)
{
$this->methodMatcher = $matcher;
$this->methodValue = $value;
}
public function getMatcher()
{
return $this->methodMatcher;
}
public function getValue()
{
return $this->methodValue;
}
}

View File

@@ -0,0 +1,312 @@
<?php
namespace Codeception\Test\Feature;
/**
* ### Usage in Codeception
*
* Since Codeception 2.3.8 this trait is enabled in `\Codeception\Test\Unit` class.
*
* ### Usage in PHPUnit
*
* Include this trait into a TestCase to be able to use Stubs and Mocks:
*
* ```php
* <?php
* class MyTest extends \PHPUnit\Framework\TestCase
* {
* use Codeception\Test\Feature\Stub;
* }
* ```
*/
trait Stub
{
private $mocks;
protected function stubStart()
{
if ($this instanceof \PHPUnit\Framework\TestCase) {
return;
}
$this->mocks = [];
}
protected function stubEnd($status, $time)
{
if ($this instanceof \PHPUnit\Framework\TestCase) {
return;
}
if ($status !== 'ok') { // Codeception status
return;
}
foreach ($this->mocks as $mockObject) {
if ($mockObject->__phpunit_hasMatchers()) {
$this->assertTrue(true); // incrementing assertions
}
$mockObject->__phpunit_verify(true);
}
}
/**
* Instantiates a class without executing a constructor.
* Properties and methods can be set as a second parameter.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* $this->make('User');
* $this->make('User', ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* $this->make(new User, ['name' => 'davert']);
* ?>
* ```
*
* To replace method provide it's name as a key in second parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* $this->make('User', ['save' => function () { return true; }]);
* $this->make('User', ['save' => true]);
* ```
*
* @param mixed $class - A class to be mocked
* @param array $params - properties and methods to set
*
* @return object - mock
* @throws \RuntimeException when class does not exist
* @throws \Exception
*/
public function make($class, $params = [])
{
return $this->mocks[] = \Codeception\Stub::make($class, $params, $this);
}
/**
* Instantiates class having all methods replaced with dummies.
* Constructor is not triggered.
* Properties and methods can be set as a second parameter.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* $this->makeEmpty('User');
* $this->makeEmpty('User', ['name' => 'davert']);
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* $this->makeEmpty(new User, ['name' => 'davert']);
* ```
*
* To replace method provide it's name as a key in second parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* $this->makeEmpty('User', ['save' => function () { return true; }]);
* $this->makeEmpty('User', ['save' => true));
* ```
*
* @param mixed $class
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
* @throws \Exception
*/
public function makeEmpty($class, $params = [])
{
return $this->mocks[] = \Codeception\Stub::makeEmpty($class, $params, $this);
}
/**
* Instantiates class having all methods replaced with dummies except one.
* Constructor is not triggered.
* Properties and methods can be replaced.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* $this->makeEmptyExcept('User', 'save');
* $this->makeEmptyExcept('User', 'save', ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* * $this->makeEmptyExcept(new User, 'save');
* ?>
* ```
*
* To replace method provide it's name as a key in second parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* $this->makeEmptyExcept('User', 'save', ['isValid' => function () { return true; }]);
* $this->makeEmptyExcept('User', 'save', ['isValid' => true]);
* ```
*
* @param mixed $class
* @param string $method
* @param array $params
*
* @return object
* @throws \Exception
*/
public function makeEmptyExcept($class, $method, $params = [])
{
return $this->mocks[] = \Codeception\Stub::makeEmptyExcept($class, $method, $params, $this);
}
/**
* Instantiates a class instance by running constructor.
* Parameters for constructor passed as second argument
* Properties and methods can be set in third argument.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* $this->construct('User', ['autosave' => false]);
* $this->construct('User', ['autosave' => false], ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* $this->construct(new User, ['autosave' => false), ['name' => 'davert']);
* ?>
* ```
*
* To replace method provide it's name as a key in third parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* $this->construct('User', [], ['save' => function () { return true; }]);
* $this->construct('User', [], ['save' => true]);
* ?>
* ```
*
* @param mixed $class
* @param array $constructorParams
* @param array $params
* @param bool|\PHPUnit\Framework\TestCase $testCase
*
* @return object
* @throws \Exception
*/
public function construct($class, $constructorParams = [], $params = [])
{
return $this->mocks[] = \Codeception\Stub::construct($class, $constructorParams, $params, $this);
}
/**
* Instantiates a class instance by running constructor with all methods replaced with dummies.
* Parameters for constructor passed as second argument
* Properties and methods can be set in third argument.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* $this->constructEmpty('User', ['autosave' => false]);
* $this->constructEmpty('User', ['autosave' => false), ['name' => 'davert']);
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* $this->constructEmpty(new User, ['autosave' => false], ['name' => 'davert']);
* ```
*
* To replace method provide it's name as a key in third parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* $this->constructEmpty('User', array(), array('save' => function () { return true; }));
* $this->constructEmpty('User', array(), array('save' => true));
* ```
*
* **To create a mock, pass current testcase name as last argument:**
*
* ```php
* <?php
* $this->constructEmpty('User', [], [
* 'save' => \Codeception\Stub\Expected::once()
* ]);
* ```
*
* @param mixed $class
* @param array $constructorParams
* @param array $params
*
* @return object
*/
public function constructEmpty($class, $constructorParams = [], $params = [])
{
return $this->mocks[] = \Codeception\Stub::constructEmpty($class, $constructorParams, $params, $this);
}
/**
* Instantiates a class instance by running constructor with all methods replaced with dummies, except one.
* Parameters for constructor passed as second argument
* Properties and methods can be set in third argument.
* Even protected and private properties can be set.
*
* ``` php
* <?php
* $this->constructEmptyExcept('User', 'save');
* $this->constructEmptyExcept('User', 'save', ['autosave' => false], ['name' => 'davert']);
* ?>
* ```
*
* Accepts either name of class or object of that class
*
* ``` php
* <?php
* $this->constructEmptyExcept(new User, 'save', ['autosave' => false], ['name' => 'davert']);
* ?>
* ```
*
* To replace method provide it's name as a key in third parameter
* and it's return value or callback function as parameter
*
* ``` php
* <?php
* $this->constructEmptyExcept('User', 'save', [], ['save' => function () { return true; }]);
* $this->constructEmptyExcept('User', 'save', [], ['save' => true]);
* ?>
* ```
*
* @param mixed $class
* @param string $method
* @param array $constructorParams
* @param array $params
*
* @return object
*/
public function constructEmptyExcept($class, $method, $constructorParams = [], $params = [])
{
return $this->mocks[] = \Codeception\Stub::constructEmptyExcept($class, $method, $constructorParams, $params, $this);
}
}

40
vendor/codeception/stub/src/shim.php vendored Normal file
View File

@@ -0,0 +1,40 @@
<?php
if (!class_exists('PHPUnit\Framework\TestCase') && class_exists('PHPUnit_Framework_TestCase')) {
class_alias('PHPUnit_Framework_TestCase', 'PHPUnit'.'\Framework\TestCase');
}
if (!class_exists('PHPUnit\Runner\Version')) {
class_alias('PHPUnit_Runner_Version', 'PHPUnit\Runner\Version');
}
if (class_exists('PHPUnit_Framework_MockObject_Generator')) {
class_alias('PHPUnit_Framework_MockObject_Generator', 'PHPUnit\Framework\MockObject\Generator');
class_alias('PHPUnit_Framework_MockObject_InvocationMocker', 'PHPUnit\Framework\MockObject\InvocationMocker');
class_alias('PHPUnit_Framework_MockObject_Invokable', 'PHPUnit\Framework\MockObject\Invokable');
class_alias('PHPUnit_Framework_MockObject_Matcher', 'PHPUnit\Framework\MockObject\Matcher');
class_alias('PHPUnit_Framework_MockObject_MockBuilder', 'PHPUnit\Framework\MockObject\MockBuilder');
if (interface_exists('PHPUnit_Framework_MockObject_MockObject')) {
/*
* old name still exists in https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/src/MockObject.php
* but namespaced alias is provided by https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/src/ForwardCompatibility/MockObject.php
*/
class_alias('PHPUnit_Framework_MockObject_MockObject', 'PHPUnit\Framework\MockObject\MockObject');
}
class_alias('PHPUnit_Framework_MockObject_Stub', 'PHPUnit\Framework\MockObject\Stub');
class_alias('PHPUnit_Framework_MockObject_Verifiable', 'PHPUnit\Framework\MockObject\Verifiable');
class_alias('PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount', 'PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount');
class_alias('PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters', 'PHPUnit\Framework\MockObject\Matcher\ConsecutiveParameters');
class_alias('PHPUnit_Framework_MockObject_Matcher_Invocation', 'PHPUnit\Framework\MockObject\Matcher\Invocation');
class_alias('PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex', 'PHPUnit\Framework\MockObject\Matcher\InvokedAtIndex');
class_alias('PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount', 'PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastCount');
class_alias('PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce', 'PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastOnce');
class_alias('PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount', 'PHPUnit\Framework\MockObject\Matcher\InvokedAtMostCount');
class_alias('PHPUnit_Framework_MockObject_Matcher_InvokedCount', 'PHPUnit\Framework\MockObject\Matcher\InvokedCount');
class_alias('PHPUnit_Framework_MockObject_Matcher_InvokedRecorder', 'PHPUnit\Framework\MockObject\Matcher\InvokedRecorder');
class_alias('PHPUnit_Framework_MockObject_Matcher_MethodName', 'PHPUnit\Framework\MockObject\Matcher\MethodName');
class_alias('PHPUnit_Framework_MockObject_Matcher_Parameters', 'PHPUnit\Framework\MockObject\Matcher\Parameters');
class_alias('PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls', 'PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls');
class_alias('PHPUnit_Framework_MockObject_Stub_Exception', 'PHPUnit\Framework\MockObject\Stub\Exception');
class_alias('PHPUnit_Framework_MockObject_Stub_ReturnArgument', 'PHPUnit\Framework\MockObject\Stub\ReturnArgument');
class_alias('PHPUnit_Framework_MockObject_Stub_ReturnCallback', 'PHPUnit\Framework\MockObject\Stub\ReturnCallback');
class_alias('PHPUnit_Framework_MockObject_Stub_Return', 'PHPUnit\Framework\MockObject\Stub\ReturnStub');
}

View File

@@ -0,0 +1,14 @@
<?php
trait ResetMocks
{
protected function resetMockObjects()
{
$refl = new ReflectionObject($this);
while (!$refl->hasProperty('mockObjects')) {
$refl = $refl->getParentClass();
}
$prop = $refl->getProperty('mockObjects');
$prop->setAccessible(true);
$prop->setValue($this, array());
}
}

View File

@@ -0,0 +1,427 @@
<?php
require_once __DIR__ .'/ResetMocks.php';
use Codeception\Stub;
class StubTest extends \PHPUnit\Framework\TestCase
{
use ResetMocks;
/**
* @var DummyClass
*/
protected $dummy;
public function setUp()
{
require_once $file = __DIR__. '/_data/DummyOverloadableClass.php';
require_once $file = __DIR__. '/_data/DummyClass.php';
$this->dummy = new DummyClass(true);
}
public function testMakeEmpty()
{
$dummy = Stub::makeEmpty('DummyClass');
$this->assertInstanceOf('DummyClass', $dummy);
$this->assertTrue(method_exists($dummy, 'helloWorld'));
$this->assertNull($dummy->helloWorld());
}
public function testMakeEmptyMethodReplaced()
{
$dummy = Stub::makeEmpty('DummyClass', array('helloWorld' => function () {
return 'good bye world';
}));
$this->assertMethodReplaced($dummy);
}
public function testMakeEmptyMethodSimplyReplaced()
{
$dummy = Stub::makeEmpty('DummyClass', array('helloWorld' => 'good bye world'));
$this->assertMethodReplaced($dummy);
}
public function testMakeEmptyExcept()
{
$dummy = Stub::makeEmptyExcept('DummyClass', 'helloWorld');
$this->assertEquals($this->dummy->helloWorld(), $dummy->helloWorld());
$this->assertNull($dummy->goodByeWorld());
}
public function testMakeEmptyExceptPropertyReplaced()
{
$dummy = Stub::makeEmptyExcept('DummyClass', 'getCheckMe', array('checkMe' => 'checked!'));
$this->assertEquals('checked!', $dummy->getCheckMe());
}
public function testMakeEmptyExceptMagicalPropertyReplaced()
{
$dummy = Stub::makeEmptyExcept('DummyClass', 'getCheckMeToo', array('checkMeToo' => 'checked!'));
$this->assertEquals('checked!', $dummy->getCheckMeToo());
}
public function testFactory()
{
$dummies = Stub::factory('DummyClass', 2);
$this->assertCount(2, $dummies);
$this->assertInstanceOf('DummyClass', $dummies[0]);
}
public function testMake()
{
$dummy = Stub::make('DummyClass', array('goodByeWorld' => function () {
return 'hello world';
}));
$this->assertEquals($this->dummy->helloWorld(), $dummy->helloWorld());
$this->assertEquals("hello world", $dummy->goodByeWorld());
}
public function testMakeMethodReplaced()
{
$dummy = Stub::make('DummyClass', array('helloWorld' => function () {
return 'good bye world';
}));
$this->assertMethodReplaced($dummy);
}
public function testMakeWithMagicalPropertiesReplaced()
{
$dummy = Stub::make('DummyClass', array('checkMeToo' => 'checked!'));
$this->assertEquals('checked!', $dummy->checkMeToo);
}
public function testMakeMethodSimplyReplaced()
{
$dummy = Stub::make('DummyClass', array('helloWorld' => 'good bye world'));
$this->assertMethodReplaced($dummy);
}
public function testCopy()
{
$dummy = Stub::copy($this->dummy, array('checkMe' => 'checked!'));
$this->assertEquals('checked!', $dummy->getCheckMe());
$dummy = Stub::copy($this->dummy, array('checkMeToo' => 'checked!'));
$this->assertEquals('checked!', $dummy->getCheckMeToo());
}
public function testConstruct()
{
$dummy = Stub::construct('DummyClass', array('checkMe' => 'checked!'));
$this->assertEquals('constructed: checked!', $dummy->getCheckMe());
$dummy = Stub::construct(
'DummyClass',
array('checkMe' => 'checked!'),
array('targetMethod' => function () {
return false;
})
);
$this->assertEquals('constructed: checked!', $dummy->getCheckMe());
$this->assertEquals(false, $dummy->targetMethod());
}
public function testConstructMethodReplaced()
{
$dummy = Stub::construct(
'DummyClass',
array(),
array('helloWorld' => function () {
return 'good bye world';
})
);
$this->assertMethodReplaced($dummy);
}
public function testConstructMethodSimplyReplaced()
{
$dummy = Stub::make('DummyClass', array('helloWorld' => 'good bye world'));
$this->assertMethodReplaced($dummy);
}
public function testConstructEmpty()
{
$dummy = Stub::constructEmpty('DummyClass', array('checkMe' => 'checked!'));
$this->assertNull($dummy->getCheckMe());
}
public function testConstructEmptyExcept()
{
$dummy = Stub::constructEmptyExcept('DummyClass', 'getCheckMe', array('checkMe' => 'checked!'));
$this->assertNull($dummy->targetMethod());
$this->assertEquals('constructed: checked!', $dummy->getCheckMe());
}
public function testUpdate()
{
$dummy = Stub::construct('DummyClass');
Stub::update($dummy, array('checkMe' => 'done'));
$this->assertEquals('done', $dummy->getCheckMe());
Stub::update($dummy, array('checkMeToo' => 'done'));
$this->assertEquals('done', $dummy->getCheckMeToo());
}
public function testStubsFromObject()
{
$dummy = Stub::make(new \DummyClass());
$this->assertInstanceOf(
'\PHPUnit\Framework\MockObject\MockObject',
$dummy
);
$dummy = Stub::make(new \DummyOverloadableClass());
$this->assertObjectHasAttribute('__mocked', $dummy);
$dummy = Stub::makeEmpty(new \DummyClass());
$this->assertInstanceOf(
'\PHPUnit\Framework\MockObject\MockObject',
$dummy
);
$dummy = Stub::makeEmpty(new \DummyOverloadableClass());
$this->assertObjectHasAttribute('__mocked', $dummy);
$dummy = Stub::makeEmptyExcept(new \DummyClass(), 'helloWorld');
$this->assertInstanceOf(
'\PHPUnit\Framework\MockObject\MockObject',
$dummy
);
$dummy = Stub::makeEmptyExcept(new \DummyOverloadableClass(), 'helloWorld');
$this->assertObjectHasAttribute('__mocked', $dummy);
$dummy = Stub::construct(new \DummyClass());
$this->assertInstanceOf(
'\PHPUnit\Framework\MockObject\MockObject',
$dummy
);
$dummy = Stub::construct(new \DummyOverloadableClass());
$this->assertObjectHasAttribute('__mocked', $dummy);
$dummy = Stub::constructEmpty(new \DummyClass());
$this->assertInstanceOf(
'\PHPUnit\Framework\MockObject\MockObject',
$dummy
);
$dummy = Stub::constructEmpty(new \DummyOverloadableClass());
$this->assertObjectHasAttribute('__mocked', $dummy);
$dummy = Stub::constructEmptyExcept(new \DummyClass(), 'helloWorld');
$this->assertInstanceOf(
'\PHPUnit\Framework\MockObject\MockObject',
$dummy
);
$dummy = Stub::constructEmptyExcept(new \DummyOverloadableClass(), 'helloWorld');
$this->assertObjectHasAttribute('__mocked', $dummy);
}
protected function assertMethodReplaced($dummy)
{
$this->assertTrue(method_exists($dummy, 'helloWorld'));
$this->assertNotEquals($this->dummy->helloWorld(), $dummy->helloWorld());
$this->assertEquals($dummy->helloWorld(), 'good bye world');
}
public static function matcherAndFailMessageProvider()
{
return array(
array(Stub\Expected::atLeastOnce(),
'Expected invocation at least once but it never'
),
array(Stub\Expected::once(),
'Method was expected to be called 1 times, actually called 0 times.'
),
array(Stub\Expected::exactly(1),
'Method was expected to be called 1 times, actually called 0 times.'
),
array(Stub\Expected::exactly(3),
'Method was expected to be called 3 times, actually called 0 times.'
),
);
}
/**
* @dataProvider matcherAndFailMessageProvider
*/
public function testExpectedMethodIsCalledFail($stubMarshaler, $failMessage)
{
$mock = Stub::makeEmptyExcept('DummyClass', 'call', array('targetMethod' => $stubMarshaler), $this);
$mock->goodByeWorld();
try {
$mock->__phpunit_verify();
$this->fail('Expected exception');
} catch (\Exception $e) {
$this->assertContains($failMessage, $e->getMessage());
}
$this->resetMockObjects();
}
public function testNeverExpectedMethodIsCalledFail()
{
$mock = Stub::makeEmptyExcept('DummyClass', 'call', array('targetMethod' => Stub\Expected::never()), $this);
$mock->goodByeWorld();
try {
$mock->call();
} catch (\Exception $e) {
$this->assertContains('was not expected to be called', $e->getMessage());
}
$this->resetMockObjects();
}
public static function matcherProvider()
{
return array(
array(0, Stub\Expected::never()),
array(1, Stub\Expected::once()),
array(2, Stub\Expected::atLeastOnce()),
array(3, Stub\Expected::exactly(3)),
array(1, Stub\Expected::once(function () {
return true;
}), true),
array(2, Stub\Expected::atLeastOnce(function () {
return array();
}), array()),
array(1, Stub\Expected::exactly(1, function () {
return null;
}), null),
array(1, Stub\Expected::exactly(1, function () {
return 'hello world!';
}), 'hello world!'),
array(1, Stub\Expected::exactly(1, 'hello world!'), 'hello world!'),
);
}
/**
* @dataProvider matcherProvider
*/
public function testMethodMatcherWithMake($count, $matcher, $expected = false)
{
$dummy = Stub::make('DummyClass', array('goodByeWorld' => $matcher), $this);
$this->repeatCall($count, array($dummy, 'goodByeWorld'), $expected);
}
/**
* @dataProvider matcherProvider
*/
public function testMethodMatcherWithMakeEmpty($count, $matcher)
{
$dummy = Stub::makeEmpty('DummyClass', array('goodByeWorld' => $matcher), $this);
$this->repeatCall($count, array($dummy, 'goodByeWorld'));
}
/**
* @dataProvider matcherProvider
*/
public function testMethodMatcherWithMakeEmptyExcept($count, $matcher)
{
$dummy = Stub::makeEmptyExcept('DummyClass', 'getCheckMe', array('goodByeWorld' => $matcher), $this);
$this->repeatCall($count, array($dummy, 'goodByeWorld'));
}
/**
* @dataProvider matcherProvider
*/
public function testMethodMatcherWithConstruct($count, $matcher)
{
$dummy = Stub::construct('DummyClass', array(), array('goodByeWorld' => $matcher), $this);
$this->repeatCall($count, array($dummy, 'goodByeWorld'));
}
/**
* @dataProvider matcherProvider
*/
public function testMethodMatcherWithConstructEmpty($count, $matcher)
{
$dummy = Stub::constructEmpty('DummyClass', array(), array('goodByeWorld' => $matcher), $this);
$this->repeatCall($count, array($dummy, 'goodByeWorld'));
}
/**
* @dataProvider matcherProvider
*/
public function testMethodMatcherWithConstructEmptyExcept($count, $matcher)
{
$dummy = Stub::constructEmptyExcept(
'DummyClass',
'getCheckMe',
array(),
array('goodByeWorld' => $matcher),
$this
);
$this->repeatCall($count, array($dummy, 'goodByeWorld'));
}
private function repeatCall($count, $callable, $expected = false)
{
for ($i = 0; $i < $count; $i++) {
$actual = call_user_func($callable);
if ($expected) {
$this->assertEquals($expected, $actual);
}
}
}
public function testConsecutive()
{
$dummy = Stub::make('DummyClass', array('helloWorld' => Stub::consecutive('david', 'emma', 'sam', 'amy')));
$this->assertEquals('david', $dummy->helloWorld());
$this->assertEquals('emma', $dummy->helloWorld());
$this->assertEquals('sam', $dummy->helloWorld());
$this->assertEquals('amy', $dummy->helloWorld());
// Expected null value when no more values
$this->assertNull($dummy->helloWorld());
}
public function testStubPrivateProperties()
{
$tester = Stub::construct(
'MyClassWithPrivateProperties',
['name' => 'gamma'],
[
'randomName' => 'chicken',
't' => 'ticky2',
'getRandomName' => function () {
return "randomstuff";
}
]
);
$this->assertEquals('gamma', $tester->getName());
$this->assertEquals('randomstuff', $tester->getRandomName());
$this->assertEquals('ticky2', $tester->getT());
}
public function testStubMakeEmptyInterface()
{
$stub = Stub::makeEmpty('\Countable', ['count' => 5]);
$this->assertEquals(5, $stub->count());
}
}
class MyClassWithPrivateProperties
{
private $name;
private $randomName = "gaia";
private $t = "ticky";
public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function getRandomName()
{
return $this->randomName;
}
public function getT()
{
return $this->t;
}
}

View File

@@ -0,0 +1,70 @@
<?php
require_once __DIR__ .'/ResetMocks.php';
class StubTraitTest extends \PHPUnit\Framework\TestCase
{
use ResetMocks;
use \Codeception\Test\Feature\Stub;
/**
* @var DummyClass
*/
protected $dummy;
public function setUp()
{
require_once $file = __DIR__. '/_data/DummyOverloadableClass.php';
require_once $file = __DIR__. '/_data/DummyClass.php';
$this->dummy = new DummyClass(true);
}
public function testMakeStubs()
{
$this->dummy = $this->make('DummyClass', ['helloWorld' => 'bye']);
$this->assertEquals('bye', $this->dummy->helloWorld());
$this->assertEquals('good bye', $this->dummy->goodByeWorld());
$this->dummy = $this->makeEmpty('DummyClass', ['helloWorld' => 'bye']);
$this->assertEquals('bye', $this->dummy->helloWorld());
$this->assertNull($this->dummy->goodByeWorld());
$this->dummy = $this->makeEmptyExcept('DummyClass', 'goodByeWorld', ['helloWorld' => 'bye']);
$this->assertEquals('bye', $this->dummy->helloWorld());
$this->assertEquals('good bye', $this->dummy->goodByeWorld());
$this->assertNull($this->dummy->exceptionalMethod());
}
public function testConstructStubs()
{
$this->dummy = $this->construct('DummyClass', ['!'], ['helloWorld' => 'bye']);
$this->assertEquals('constructed: !', $this->dummy->getCheckMe());
$this->assertEquals('bye', $this->dummy->helloWorld());
$this->assertEquals('good bye', $this->dummy->goodByeWorld());
$this->dummy = $this->constructEmpty('DummyClass', ['!'], ['helloWorld' => 'bye']);
$this->assertNull($this->dummy->getCheckMe());
$this->assertEquals('bye', $this->dummy->helloWorld());
$this->assertNull($this->dummy->goodByeWorld());
$this->dummy = $this->constructEmptyExcept('DummyClass', 'getCheckMe', ['!'], ['helloWorld' => 'bye']);
$this->assertEquals('constructed: !', $this->dummy->getCheckMe());
$this->assertEquals('bye', $this->dummy->helloWorld());
$this->assertNull($this->dummy->goodByeWorld());
$this->assertNull($this->dummy->exceptionalMethod());
}
public function testMakeMocks()
{
$this->dummy = $this->make('DummyClass', [
'helloWorld' => \Codeception\Stub\Expected::once()
]);
$this->dummy->helloWorld();
try {
$this->dummy->helloWorld();
} catch (Exception $e) {
$this->assertContains('was not expected to be called more than once', $e->getMessage());
$this->resetMockObjects();
return;
}
$this->fail('No exception thrown');
}
}

View File

@@ -0,0 +1,67 @@
<?php
class DummyClass
{
protected $checkMe = 1;
protected $properties = array('checkMeToo' => 1);
function __construct($checkMe = 1)
{
$this->checkMe = "constructed: ".$checkMe;
}
public function helloWorld() {
return "hello";
}
public function goodByeWorld() {
return "good bye";
}
protected function notYourBusinessWorld()
{
return "goAway";
}
public function getCheckMe() {
return $this->checkMe;
}
public function getCheckMeToo() {
return $this->checkMeToo;
}
public function call() {
$this->targetMethod();
return true;
}
public function targetMethod() {
return true;
}
public function exceptionalMethod() {
throw new Exception('Catch it!');
}
public function __set($name, $value) {
if ($this->isMagical($name)) {
$this->properties[$name] = $value;
}
}
public function __get($name) {
if ($this->__isset($name)) {
return $this->properties[$name];
}
}
public function __isset($name) {
return $this->isMagical($name) && isset($this->properties[$name]);
}
private function isMagical($name) {
$reflectionClass = new \ReflectionClass($this);
return !$reflectionClass->hasProperty($name);
}
}

View File

@@ -0,0 +1,68 @@
<?php
class DummyOverloadableClass
{
protected $checkMe = 1;
protected $properties = array('checkMeToo' => 1);
function __construct($checkMe = 1)
{
$this->checkMe = "constructed: ".$checkMe;
}
public function helloWorld() {
return "hello";
}
public function goodByeWorld() {
return "good bye";
}
protected function notYourBusinessWorld()
{
return "goAway";
}
public function getCheckMe() {
return $this->checkMe;
}
public function getCheckMeToo() {
return $this->checkMeToo;
}
public function call() {
$this->targetMethod();
return true;
}
public function targetMethod() {
return true;
}
public function exceptionalMethod() {
throw new Exception('Catch it!');
}
public function __get($name) {
//seeing as we're not implementing __set here, add check for __mocked
$return = null;
if ($name === '__mocked') {
$return = isset($this->__mocked) ? $this->__mocked : null;
} else {
if ($this->__isset($name)) {
$return = $this->properties[$name];
}
}
return $return;
}
public function __isset($name) {
return $this->isMagical($name) && isset($this->properties[$name]);
}
private function isMagical($name) {
$reflectionClass = new \ReflectionClass($this);
return !$reflectionClass->hasProperty($name);
}
}