Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00%
0 / 1
0.00%
0 / 35
CRAP
0.00%
0 / 323
Unit
0.00%
0 / 1
0.00%
0 / 35
8742
0.00%
0 / 323
 _initialize()
0.00%
0 / 1
2
0.00%
0 / 3
 _before(\Codeception\TestCase $test)
0.00%
0 / 1
2
0.00%
0 / 5
 _after(\Codeception\TestCase $test)
0.00%
0 / 1
2
0.00%
0 / 4
 _failed(\Codeception\TestCase $test, $fail)
0.00%
0 / 1
20
0.00%
0 / 10
 testMethod($signature)
0.00%
0 / 1
20
0.00%
0 / 17
 haveFakeClass($instance)
0.00%
0 / 1
6
0.00%
0 / 6
 haveStub($instance)
0.00%
0 / 1
2
0.00%
0 / 4
 executeTestedMethodOn($object)
0.00%
0 / 1
20
0.00%
0 / 19
 anonymous function ()
0.00%
0 / 1
2
0.00%
0 / 2
 executeTestedMethodWith($params)
0.00%
0 / 1
6
0.00%
0 / 7
 executeTestedMethod()
0.00%
0 / 1
6
0.00%
0 / 9
 execute(\Closure $code)
0.00%
0 / 1
6
0.00%
0 / 13
 executeMethod($object, $method)
0.00%
0 / 1
6
0.00%
0 / 15
 changeProperties($obj, $values = array()
0.00%
0 / 1
12
0.00%
0 / 12
 changeProperty($obj, $property, $value)
0.00%
0 / 1
2
0.00%
0 / 3
 seeExceptionThrown($classname, $message = null)
0.00%
0 / 1
20
0.00%
0 / 10
 predictExceptions()
0.00%
0 / 1
72
0.00%
0 / 19
 catchException($e)
0.00%
0 / 1
12
0.00%
0 / 8
 createMocks()
0.00%
0 / 1
506
0.00%
0 / 56
 seeMethodInvoked($mock, $method, array $params = array()
0.00%
0 / 1
2
0.00%
0 / 4
 seeMethodInvokedOnce($mock, $method, array $params = array()
0.00%
0 / 1
2
0.00%
0 / 4
 seeMethodNotInvoked($mock, $method, array $params = array()
0.00%
0 / 1
2
0.00%
0 / 4
 seeMethodInvokedMultipleTimes($mock, $method, $times, array $params = array()
0.00%
0 / 1
2
0.00%
0 / 4
 verifyMock($mock)
0.00%
0 / 1
56
0.00%
0 / 21
 seeResultEquals($value)
0.00%
0 / 1
2
0.00%
0 / 4
 seeResultContains($value)
0.00%
0 / 1
2
0.00%
0 / 4
 dontSeeResultContains($value)
0.00%
0 / 1
2
0.00%
0 / 4
 dontSeeResultEquals($value)
0.00%
0 / 1
2
0.00%
0 / 4
 seeEmptyResult()
0.00%
0 / 1
2
0.00%
0 / 4
 seeResultIs($type)
0.00%
0 / 1
6
0.00%
0 / 8
 seePropertyEquals($object, $property, $value)
0.00%
0 / 1
2
0.00%
0 / 6
 seePropertyIs($object, $property, $type)
0.00%
0 / 1
6
0.00%
0 / 8
 seeMethodReturns($object, $method, $value, $params = array()
0.00%
0 / 1
2
0.00%
0 / 5
 seeMethodNotReturns($object, $method, $value, $params = array()
0.00%
0 / 1
2
0.00%
0 / 5
 retrieveProperty($object, $property)
0.00%
0 / 1
6
0.00%
0 / 8
<?php
namespace Codeception\Module;
/**
* Unit testing module
*
* This is the heart of the CodeGuy testing framework.
* By providing a unique set of features Unit, the module makes your tests cleaner, more readable, and easier to write.
*
* ## Status
*
* * Maintainer: **davert**
* * Stability: **stable**
* * Contact: codecept@davert.mail.ua
*
* ## Features
* * Descriptive - simply write what you are testing and how you are testing.
* * Execution limit - only 'execute* methods actually execute your code. It's easy to see where tested methods are invoked.
* * Simple stub definition - create stubbed class with one call. All properties and methods can be passed as callable functions.
* * Dynamic mocking - stubs can be automatically turned into mocks.
*
*/
class Unit extends \Codeception\Module
{
protected $stubs = array();
protected $predictedExceptions = array();
protected $thrownExceptions = array();
protected $last_result;
/**
* @var \Codeception\TestCase
*/
protected $test;
protected $testedClass;
protected $testedMethod;
protected $testedStatic;
public function _initialize()
{
}
public function _before(\Codeception\TestCase $test)
{
$this->test = $test;
$this->stubs = array();
}
public function _after(\Codeception\TestCase $test)
{
\Codeception\Util\Fixtures::cleanup();
}
public function _failed(\Codeception\TestCase $test, $fail)
{
if (count($this->stubs)) {
$this->debug("Stubs were used:");
foreach ($this->stubs as $stub) {
if (isset($stub->__mocked)) $this->debug($stub->__mocked);
$this->debug(json_encode($stub));
}
}
}
/**
* Registers a class/method which will be tested.
* When you run 'execute' this method will be invoked.
* Please, note that it also updates the feature section of the scenario.
*
* For non-static methods:
*
* ``` php
* <?php
* $I->testMethod('ClassName.MethodName'); // I will need ClassName instance for this
* ```
*
* For static methods:
*
* ``` php
* <?php
* $I->testMethod('ClassName::MethodName');
* ```
*
* @param $signature
*/
public function testMethod($signature)
{
if (strpos($signature, '.')) {
// this is class method
list($class, $method) = explode('.', $signature);
$this->testedClass = $class;
$this->testedMethod = $method;
$this->testedStatic = false;
} elseif (strpos($signature, '::')) {
// we test static method
list($class, $method) = explode('::', $signature);
$this->testedClass = $class;
$this->testedMethod = $method;
$this->testedStatic = true;
}
$this->debug('Class: ' . $class);
$this->debug('Method: ' . $method);
if ($this->testedStatic) $this->debug('Static');
}
/**
* Adds a stub to the internal registry.
* Use this command if you need to convert this stub to a mock.
* Without adding the stub to registry you can't trace it's method invocations.
*
* @param $instance
*/
public function haveFakeClass($instance)
{
$this->stubs[] = $instance;
$stubid = count($this->stubs) - 1;
if (isset($instance->__mocked)) $this->debugSection('Registered stub', 'Stub_' . $stubid . ' {' . $instance->__mocked . '}');
}
/**
* Alias for haveFakeClass
*
* @alias haveFakeClass
* @param $instance
*/
public function haveStub($instance)
{
$this->haveFakeClass($instance);
}
/**
* Execute The tested method on an object (a stub can be passed).
* First argument is an object, the rest are supposed to be parameters passed to method.
*
* Example:
*
* ``` php
* <?php
* $I->wantTo('authenticate user');
* $I->testMethod('User.authenticate');
* $user = new User();
* $I->executeTestedMethodOn($user, 'Davert','qwerty');
* // This line $user->authenticate('Davert','qwerty') was called.
* $I->seeResultEquals(true);
* ?>
* ```
*
* For static methods use 'executeTestedMethodWith'.
*
* @param $object
*/
public function executeTestedMethodOn($object)
{
$args = func_get_args();
$obj = array_shift($args);
$method = $this->testedMethod;
$callable = function () use ($method, $obj, $args) {
$reflectedObj = new \ReflectionClass($obj);
$reflectedMethod = $reflectedObj->getMethod($method);
if (!$reflectedMethod)
throw new \Codeception\Exception\Module(__CLASS__,sprintf('Method %s can\'t be called in this object', $method));
if (!$reflectedMethod->isPublic()) {
$reflectedMethod->setAccessible(true);
}
return $reflectedMethod->invokeArgs($obj, $args);
};
$this->execute($callable);
if (isset($obj->__mocked)) $this->debug('Received Stub');
$this->debug("Method {$this->testedMethod} executed");
$this->debug('With parameters: ' . json_encode($args));
}
/**
* Executes the tested static method with parameters provided.
*
* ```
* <?php
* $I->testMethod('User::validateName');
* $I->executeTestedMethodWith('davert',true);
* // This line User::validate('davert', true); was called
* ?>
* ```
* For a non-static method use 'executeTestedMethodOn'
*
* @param $params
* @throws \Codeception\Exception\Module
*/
public function executeTestedMethodWith($params)
{
$args = func_get_args();
if (!method_exists($this->testedClass, $this->testedMethod))
throw new \Codeception\Exception\Module(__CLASS__,sprintf('%s::%s is not valid callable', $this->testedClass, $this->testedMethod));
$class = $this->testedClass;
$method = $this->testedMethod;
$callable = function () use ($args, $class, $method) {
return call_user_func_array(array($class, $method), $args);
};
$this->execute($callable);
$this->debug("Static method {$this->testedClass}::{$this->testedMethod} executed");
$this->debug('With parameters: ' . json_encode($args));
}
/**
* Executes the method which is being tested.
* If the method is not static, the class instance should be provided.
*
* If a method is static 'executeTestedWith' will be called.
* If a method is not static 'executeTestedOn' will be called.
* See those methods for the full reference
*
* @throws \InvalidArgumentException
*/
public function executeTestedMethod()
{
$args = func_get_args();
if ($this->testedStatic) {
call_user_func_array(array($this, 'executeTestedMethodWith'), $args);
} else {
call_user_func_array(array($this, 'executeTestedMethodOn'), $args);
}
}
/**
* Executes a code block. The result of execution will be stored.
* Parameter should be a valid Closure. The returned value can be checked with seeResult actions.
*
* Example:
*
* ``` php
* <?php
* $user = new User();
* $I->execute(function() use ($user) {
* $user->setName('Davert');
* return $user->getName();
* });
* $I->seeResultEquals('Davert');
* ?>
* ```
*
* You can use native PHPUnit asserts in the executed code.
* These can be either static methods of the 'PHPUnit_Framework_assert' class,
* or functions taken from 'PHPUnit/Framework/Assert/Functions.php'. They start with 'assert_' prefix.
* You should manually include this file, as these functions may conflict with functions in your code.
*
* Example:
*
* ``` php
* <?php
* require_once 'PHPUnit/Framework/Assert/Functions.php';
*
* $user = new User();
* $I->execute(function() use ($user) {
* $user->setName('Davert');
* assertEquals('Davert', $user->getName());
* });
* ```
*
* @param \Closure $code
*/
public function execute(\Closure $code)
{
// cleanup mocks
// foreach ($this->stubs as $mock) {
// $mock->__phpunit_cleanup();
// }
$this->createMocks();
$this->predictExceptions();
$res = null;
try {
$res = $code->__invoke();
} catch (\Exception $e) {
$this->catchException($e);
}
$this->debug('Result: ' . json_encode($res));
$this->last_result = $res;
}
/**
* Executes a method of an object.
* Additional parameters can be provided.
*
* Example:
*
* ``` php
* <?php
* // to execute $user->getName()
* $I->executeMethod($user,'getName');
*
* // to execute $user->setName('davert');
* $I->executeMethod($user,'setName', 'davert');
*
* // or more parameters
* $I->executeMethod($user, 'setNameAndAge', 'davert', '30');
*
* ?>
* ```
*
* @param $object
* @param $method
*/
public function executeMethod($object, $method) {
$params = func_get_args();
$object = array_shift($params);
$method = array_shift($params);
// cleanup mocks
// foreach ($this->stubs as $mock) {
// $mock->__phpunit_cleanup();
// }
$this->createMocks();
$this->predictExceptions();
$res = null;
try {
$res = call_user_func_array(array($object, $method), $params);
} catch (\Exception $e) {
$this->catchException($e);
}
$this->debug('Result: ' . json_encode($res));
$this->last_result = $res;
}
/**
* Updates multiple properties of the selected object.
* Can update even private and protected properties.
*
* Properties to be updated and their values are passed in the second parameter as an array:
* array('theProperty' => 'some value',
* ('anotherProperty' => 'another value')
*
* @param $obj
* @param array $values
*/
public function changeProperties($obj, $values = array()) {
//As for now, PHP cannot resolve properties of namespaced class if class instance passed to reflector
//In our case we have mocked class with original full classname stored in __mocked property
if(isset($obj->__mocked)) {
$reflectedObj = new \ReflectionClass($obj->__mocked);
} else {
$reflectedObj = new \ReflectionClass($obj);
}
foreach ($values as $key => $val) {
$property = $reflectedObj->getProperty($key);
$property->setAccessible(true);
$property->setValue($obj, $val);
}
}
/**
* Updates a single property of the selected object
* Can update even private and protected properties.
*
* @param $obj
* @param $property
* @param $value
*/
public function changeProperty($obj, $property, $value) {
$this->changeProperties($obj, array($property => $value));
}
public function seeExceptionThrown($classname, $message = null) {
\PHPUnit_Framework_Assert::assertNotEmpty($this->thrownExceptions, "No exception was thrown");
foreach ($this->thrownExceptions as $e) {
if ($e instanceof $classname) {
\PHPUnit_Framework_Assert::assertInstanceOf($classname, $e);
if ($message) \PHPUnit_Framework_Assert::assertContains($message, $e->getMessage());
return;
}
}
}
protected function predictExceptions()
{
$this->thrownExceptions = array();
$this->predictedExceptions = array();
$scenario = $this->test->getScenario();
$steps = $scenario->getSteps();
for ($i = $scenario->getCurrentStep() + 1; $i < count($steps); $i++) {
$step = $steps[$i];
$action = $step->getAction();
if ($action == 'executeTestedMethod') break;
if ($action == 'executeTestedMethodOn') break;
if ($action == 'executeTestedMethodWith') break;
if ($action == 'execute') break;
if ($action == 'executeMethod') break;
if ($action != 'seeExceptionThrown') continue;
$args = $step->getArguments(false);
$this->predictedExceptions[] = $args[0];
}
}
protected function catchException($e)
{
$this->thrownExceptions[] = $e;
foreach ($this->predictedExceptions as $predicted) {
if ($e instanceof $predicted) return;
}
throw $e;
}
/**
* Very magical function that generates Mock methods for expected assertions
* Allows the declaration of seeMethodInvoked, seeMethodNotInvoked, etc, AFTER the 'execute' command
*
*/
protected function createMocks()
{
$scenario = $this->test->getScenario();
$steps = $scenario->getSteps();
if (!isset($steps[$scenario->getCurrentStep()])) throw new \Exception("New steps were added to scenario in realtime. Can't proceed.\nRemove loops from your unit test to fix it");
for ($i = $scenario->getCurrentStep()+1; $i < count($steps); $i++) {
$step = $steps[$i];
if (strpos($action = $step->getAction(), 'seeMethod') === 0) {
$arguments = $step->getArguments(false);
$mock = array_shift($arguments);
$function = array_shift($arguments);
$params = array_shift($arguments);
foreach ($this->stubs as $stub) {
if (get_class($stub) == get_class($mock)) {
$mock = $stub;
}
}
$invoke = false;
switch ($action) {
case 'seeMethodInvoked':
case 'seeMethodInvokedAtLeastOnce':
if (!$mock) throw new \InvalidArgumentException("Stub class not defined");
$invoke = new \PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce();
break;
case 'seeMethodInvokedOnce':
if (!$mock) throw new \InvalidArgumentException("Stub class not defined");
$invoke = new \PHPUnit_Framework_MockObject_Matcher_InvokedCount(1);
break;
case 'seeMethodNotInvoked':
if (!$mock) throw new \InvalidArgumentException("Stub class not defined");
$invoke = new \PHPUnit_Framework_MockObject_Matcher_InvokedCount(0);
break;
case 'seeMethodInvokedMultipleTimes':
if (!$mock) throw new \InvalidArgumentException("Stub class not defined");
$times = $params;
if (!is_int($times)) throw new \InvalidArgumentException("Invoked times count should be an integer");
$params = $arguments;
$invoke = new \PHPUnit_Framework_MockObject_Matcher_InvokedCount($times);
break;
default:
}
if ($invoke) {
$mockMethod = $mock->expects($invoke)->method($function);
$this->debug(get_class($invoke) . ' attached');
if ($params) {
call_user_func_array(array($mockMethod, 'with'), $params);
$this->debug('with ' . json_encode($params));
}
}
}
if ($step->getAction() == 'executeTestedMethod') break;
if ($step->getAction() == 'execute') break;
if ($step->getAction() == 'executeTestedMethodOn') break;
if ($step->getAction() == 'executeTestedMethodWith') break;
}
}
/**
* Checks that a method of a stub was invoked after the last execution.
* Requires a stub as the first parameter, the method name as the second.
* Optionally pass the arguments which are expected by the executed method.
*
* Example:
*
* ``` php
* <?php
* $I->testMethod('UserService.create');
* $I->haveStub($user = Stub::make('Model\User'));*
* $service = new UserService($user);
* $I->executeTestedMethodOn($service);
* // we expect $user->save was invoked.
* $I->seeMethodInvoked($user, 'save');
* ?>
* ```
*
* This method dynamically creates a mock from a stub.
*
* @magic
* @see createMocks
* @param $mock
* @param $method
* @param array $params
*/
public function seeMethodInvoked($mock, $method, array $params = array())
{
$this->verifyMock($mock);
}
/**
* Checks that a method of a stub was invoked *only once* after the last execution.
* Requires a stub as the first parameter, a method name as the second.
* Optionally pass the arguments which are expected by the executed method.
*
* Look for 'seeMethodInvoked' to see the example.
* @magic
* @see createMocks
* @param $mock
* @param $method
* @param array $params
*/
public function seeMethodInvokedOnce($mock, $method, array $params = array())
{
$this->verifyMock($mock);
}
/**
* Checks that a method of a stub *was not invoked* after the last execution.
* Requires a stub as the first parameter, a method name as the second.
* Optionally pass the arguments which are expected by the executed method.
* @magic
* @see createMocks
* @param $mock
* @param $method
* @param array $params
*/
public function seeMethodNotInvoked($mock, $method, array $params = array())
{
$this->verifyMock($mock);
}
/**
* Checks that a method of a stub was invoked *multiple times* after the last execution.
* Requires a stub as the first parameter, a method name as the second and the expected number of executions.
* Optionally pass the arguments which are expected by the executed method.
*
* Look for 'seeMethodInvoked' to see the example.
* @magic
* @see createMocks
* @param $mock
* @param $method
* @param $times
* @param array $params
*/
public function seeMethodInvokedMultipleTimes($mock, $method, $times, array $params = array())
{
$this->verifyMock($mock);
}
protected function verifyMock($mock)
{
if ($mock instanceof \Codeception\Maybe) $mock = $mock->__value();
foreach ($this->stubs as $stubid => $stub) {
if (spl_object_hash($stub) == spl_object_hash($mock)) {
if (!$mock->__phpunit_hasMatchers()) {
continue;
// throw new \Exception("Probably Internal Error. There is no matchers for current mock");
}
if (isset($stub->__mocked)) {
$this->debugSection('Triggered Stub', 'Stub_' . $stubid . ' {' . $stub->__mocked . '}');
}
\PHPUnit_Framework_Assert::assertTrue(true); // hook to increment assertions counter
try {
$mock->__phpunit_verify();
} catch (\PHPUnit_Framework_ExpectationFailedException $e) {
\PHPUnit_Framework_Assert::fail("\n" . $e->getMessage()); // hook to increment assertions counter
throw $e;
}
return;
}
}
throw new \Exception("Mock is not registered by 'haveStub' or 'haveFakeClass' methods");
}
/**
* Asserts that the last result from the tested method is equal to value
*
* @param $value
*/
public function seeResultEquals($value)
{
$this->assert(array('Equals', $value, $this->last_result,'in '.$this->last_result));
}
public function seeResultContains($value)
{
\PHPUnit_Framework_Assert::assertContains($value, $this->last_result);
}
/**
* Checks that the result of the last execution doesn't contain a value.
*
* @param $value
*/
public function dontSeeResultContains($value)
{
\PHPUnit_Framework_Assert::assertNotContains($value, $this->last_result);
}
/**
* Checks that the result of the last execution is not equal to a value.
*
* @param $value
*/
public function dontSeeResultEquals($value)
{
\PHPUnit_Framework_Assert::assertNotEquals($value, $this->last_result);
}
/**
* Checks that the result of the last execution is empty.
*/
public function seeEmptyResult()
{
\PHPUnit_Framework_Assert::assertEmpty($this->last_result);
}
/**
* Checks that the result of the last execution is a specific type.
* Either 'int', 'bool', 'string', 'array', 'float', 'null', 'resource', 'scalar' can be passed for simple types.
* Otherwise the parameter must be a class and the result must be an instance of that class.
*
* Example:
*
* ``` php
* <?php
* $I->execute(function() { return new User });
* $I->seeResultIs('User');
* ?>
* ```
*
* @param $type
*/
public function seeResultIs($type)
{
if (in_array($type, array('int', 'bool', 'string', 'array', 'float', 'null', 'resource', 'scalar'))) {
\PHPUnit_Framework_Assert::assertInternalType($type, $this->last_result);
return;
}
\PHPUnit_Framework_Assert::assertInstanceOf($type, $this->last_result);
}
/**
* Checks that the property of an object equals the value provided.
* Can check even protected or private properties.
*
* Bear in mind that testing non-public properties is not a good practice.
* Use it only if you have no other way to test it.
*
* @param $object
* @param $property
* @param $value
*/
public function seePropertyEquals($object, $property, $value)
{
$current = $this->retrieveProperty($object, $property);
$this->debug('Property value is: ' . $current);
\PHPUnit_Framework_Assert::assertEquals($value, $current);
}
/**
* Checks that the property is a passed type.
* Either 'int', 'bool', 'string', 'array', 'float', 'null', 'resource', 'scalar' can be passed for simple types.
* Otherwise the parameter must be a class and the property must be an instance of that class.
*
* Bear in mind that testing non-public properties is not a good practice.
* Use it only if you have no other way to test it.
*
* @param $object
* @param $property
* @param $type
*/
public function seePropertyIs($object, $property, $type) {
$current = $this->retrieveProperty($object, $property);
if (in_array($type, array('int', 'bool', 'string', 'array', 'float', 'null', 'resource', 'scalar'))) {
\PHPUnit_Framework_Assert::assertInternalType($type, $current);
return;
}
\PHPUnit_Framework_Assert::assertInstanceOf($type, $current);
}
/**
* Executes a method and checks that the result is equal to a value.
* Good for testing values taken from getters.
*
* Example:
*
* ``` php
* $I->testMethod('User.setName');
* $user = new User();
* $I->executeTestedMethodOn($user, 'davert');
* $I->seeMethodReturns($user,'getName','davert');
*
* ```
* *
* @param $object
* @param $method
* @param $value
* @param array $params
*/
public function seeMethodReturns($object, $method, $value, $params = array())
{
$result = call_user_func_array(array($object, $method), $params);
\PHPUnit_Framework_Assert::assertEquals($value, $result);
}
/**
* Executes a method and checks that the result is NOT equal to a value.
* Good for testing values taken from getters.
*
* Look for 'seeMethodReturns' for example.
*
* @param $object
* @param $method
* @param $value
* @param array $params
*/
public function seeMethodNotReturns($object, $method, $value, $params = array())
{
$result = call_user_func_array(array($object, $method), $params);
\PHPUnit_Framework_Assert::assertNotEquals($value, $result);
}
protected function retrieveProperty($object, $property)
{
if (isset($object->__mocked)) $this->debug('Received STUB');
$reflectionClass = new \ReflectionClass($object);
$reflectionProperty = $reflectionClass->getProperty($property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($object);
}