Skip to content

Commit

Permalink
Merge pull request BedrockStreaming#7 from M6Web/more-flexibility
Browse files Browse the repository at this point in the history
Adding possibility to instantiate itself the mock
  • Loading branch information
KuiKui committed Dec 18, 2013
2 parents 4e03972 + 9adcff0 commit 79685a3
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 29 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,16 @@ It also mocks **PIPELINE** and **EXECUTE** functions but without any transaction
RedisMock library provides a factory able to build a mocked class of your Redis library that can be directly injected in your application :

```php
$factory = new \M6Web\Component\RedisMockFactory();
$myRedisMock = $factory->getAdapter('My\Redis\Library', new \M6Web\Component\RedisMock\RedisMock());
$factory = new \M6Web\Component\RedisMock\RedisMockFactory();
$myRedisMockClass = $factory->getAdapterClass('My\Redis\Library');
$myRedisMock = new $myRedisMockClass($myParameters);
```

In a simpler way, if you don't need to instanciate the mocked class with custom parameters (e.g. to easier inject the mock using Symfony config file), you can use `getAdapter` instead of `getAdapterClass` to directly create the adapter :

```php
$factory = new \M6Web\Component\RedisMock\RedisMockFactory();
$myRedisMock = $factory->getAdapter('My\Redis\Library');
```

**WARNING !** *RedisMock doesn't implement all Redis features and commands. The mock can have undesired behavior if your parent class uses unsupported features.*
Expand Down
73 changes: 54 additions & 19 deletions src/M6Web/Component/RedisMock/RedisMockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* The mock can have undesired behavior if your parent class uses unsupported features.
*
* @author Adrien Samson <[email protected]>
* @author Florent Dubost <[email protected]>
*/
class RedisMockFactory
{
Expand Down Expand Up @@ -158,24 +159,35 @@ class RedisMockFactory
);

protected $classTemplate = <<<'CLASS'
namespace {{namespace}};
class {{class}} extends \{{baseClass}}
{
protected $mock;
public function __construct($mock)
protected $clientMock;
public function setClientMock($clientMock)
{
$this->clientMock = $clientMock;
}
public function getClientMock()
{
$this->mock = $mock;
if (!isset($this->clientMock)) {
$this->clientMock = new RedisMock();
}
return $this->clientMock;
}
public function __call($method, $args)
{
$methodName = strtolower($method);
if (!method_exists('M6Web\Component\RedisMock\RedisMock', $methodName)) {
throw new \M6Web\Component\RedisMock\UnsupportedException(sprintf('Redis command `%s` is not supported by RedisMock.', $methodName));
throw new UnsupportedException(sprintf('Redis command `%s` is not supported by RedisMock.', $methodName));
}
return call_user_func_array(array($this->mock, $methodName), $args);
return call_user_func_array(array($this->getClientMock(), $methodName), $args);
}
{{methods}}
}
Expand All @@ -185,38 +197,61 @@ public function __call($method, $args)
public function {{method}}({{signature}})
{
return $this->mock->{{method}}({{args}});
return $this->getClientMock()->{{method}}({{args}});
}

METHOD;

protected $constructorTemplate = <<<'CONSTRUCTOR'
public function getAdapter($classToExtend, $redisMock)
public function __construct()
{
$newClassName = sprintf('RedisMock_%s_Adapter', str_replace('\\', '_', $classToExtend));
$namespace = __NAMESPACE__;
$class = $namespace . '\\'. $newClassName;
}
CONSTRUCTOR;


if (class_exists($class)) {
return new $class($redisMock);
public function getAdapter($classToExtend)
{
list($namespace, $newClassName, $class) = $this->getAdapterClassName($classToExtend);

if (!class_exists($class)) {
$classCode = $this->getClassCode($namespace, $newClassName, new \ReflectionClass($classToExtend), true);
eval($classCode);
}

return new $class();
}

public function getAdapterClass($classToExtend)
{
list($namespace, $newClassName, $class) = $this->getAdapterClassName($classToExtend, '_NativeConstructor');

if (!class_exists($class)) {
$classCode = $this->getClassCode($namespace, $newClassName, new \ReflectionClass($classToExtend));
eval($classCode);
}

$classCode = $this->getClassCode($namespace, $newClassName, new \ReflectionClass($classToExtend));
return $class;
}

eval($classCode);
protected function getAdapterClassName($classToExtend, $suffix = '')
{
$newClassName = sprintf('RedisMock_%s_Adapter%s', str_replace('\\', '_', $classToExtend), $suffix);
$namespace = __NAMESPACE__;
$class = $namespace . '\\'. $newClassName;

return new $class($redisMock);
return [$namespace, $newClassName, $class];
}

protected function getClassCode($namespace, $newClassName, \ReflectionClass $class)
protected function getClassCode($namespace, $newClassName, \ReflectionClass $class, $orphanizeConstructor = false)
{
$methodsCode = '';
$methodsCode = $orphanizeConstructor ? $this->constructorTemplate : '';

foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
$methodName = strtolower($method->getName());

if (!method_exists('M6Web\Component\RedisMock\RedisMock', $methodName) && in_array($methodName, $this->redisCommands)) {
throw new \M6Web\Component\RedisMock\UnsupportedException(sprintf('Redis command `%s` is not supported by RedisMock.', $methodName));
throw new UnsupportedException(sprintf('Redis command `%s` is not supported by RedisMock.', $methodName));
} elseif (method_exists('M6Web\Component\RedisMock\RedisMock', $methodName)) {
$methodsCode .= strtr($this->methodTemplate, array(
'{{method}}' => $methodName,
Expand Down
57 changes: 49 additions & 8 deletions tests/units/RedisMockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
use mageekguy\atoum\test;

/**
* test class for RedisMockFactory
* Test class for RedisMockFactory
*/
class RedisMockFactory extends test
{
/**
* test the mock
* Test the mock
*
* @return void
*/
public function testMock()
{
$factory = new Factory();
$mock = $factory->getAdapter('StdClass', new Mock());
$mock = $factory->getAdapter('StdClass');

$this->assert
->object($mock)
Expand Down Expand Up @@ -48,7 +49,7 @@ public function testMock()
})
->isInstanceOf('\M6Web\Component\RedisMock\UnsupportedException');

$mock2 = $factory->getAdapter('StdClass', new Mock());
$mock2 = $factory->getAdapter('StdClass');

$this->assert
->object($mock2)
Expand All @@ -58,13 +59,14 @@ public function testMock()
}

/**
* test the mock with a complex base class
* Test the mock with a complex base class
*
* @return void
*/
public function testMockComplex()
{
$factory = new Factory();
$mock = $factory->getAdapter('M6Web\Component\RedisMock\tests\units\RedisWithMethods', new Mock());
$mock = $factory->getAdapter('M6Web\Component\RedisMock\tests\units\RedisWithMethods');

$this->assert
->object($mock)
Expand Down Expand Up @@ -101,13 +103,44 @@ public function testMockComplex()
));
}

/**
* Test the mock with a base class that implement unsupported Redis commands
*
* @return void
*/
public function testUnsupportedMock()
{
$factory = new Factory();
$this->assert
->exception(function() use ($factory) {
$factory->getAdapter('M6Web\Component\RedisMock\Adapter\tests\units\RedisWithUnsupportedMethods', new Mock());
});
$factory->getAdapter('M6Web\Component\RedisMock\tests\units\RedisWithUnsupportedMethods');
})
->isInstanceOf('\M6Web\Component\RedisMock\UnsupportedException');
}

/**
* Test method getAdpaterClass
*
* @return void
*/
public function testGetAdapterClass()
{
$factory = new Factory();
$this->assert
->string($class = $factory->getAdapterClass('M6Web\Component\RedisMock\tests\units\RedisWithNativeConstructor'))
->isEqualTo('M6Web\Component\RedisMock\RedisMock_M6Web_Component_RedisMock_tests_units_RedisWithNativeConstructor_Adapter_NativeConstructor')
->class($class)
->extends('M6Web\Component\RedisMock\tests\units\RedisWithNativeConstructor')
->when(function() use ($class) {
$mock = new $class();
})
->error()
->exists()
->when(function() use ($class) {
$mock = new $class(null);
})
->error()
->notExists();
}
}

Expand Down Expand Up @@ -145,4 +178,12 @@ public function punsubscribe($pattern = null)
{
throw new \Exception('Not mocked');
}
}

class RedisWithNativeConstructor
{
public function __construct($param)
{

}
}

0 comments on commit 79685a3

Please sign in to comment.