动态覆盖父类

时间:2016-10-28 16:47:24

标签: php unit-testing dynamic mocking

我试图建立一个动态模拟(松散地使用术语)有几个原因:

  1. 练习将帮助我了解有关测试的更多信息。
  2. 大多数模拟系统对我的需求做得太多了。
  3. 大多数嘲弄系统都不会以我喜欢的方式做我需要的事情。
  4. 换句话说:请不要告诉我去使用模拟库。我已经使用了模拟库(我已经广泛使用了至少三个PHP库),我决定尝试创建自己的解决方案是有意识的。

    所以:我试图做一些概念上看起来相当简单的事情。

    如何动态覆盖模拟类中存在于mock扩展类中的所有方法?

    换句话说,如果我有一个包含方法A的类a,并且我有一个扩展类B的模拟A,我该如何捕获所有调用方法A没有在模拟类a中显式实现方法B

    我已尝试使用__call()魔术方法执行此操作,但这不会起作用,因为__call()仅捕获对不存在的方法的调用。< / p>

    我想避免需要进行大规模架构更改的方法。我的主要要求是,在构造函数中需要类A实例的任何类都不能告诉mock B不是类A的实例。因此,我对模拟课程B的初步选择扩展了课程A。我也不必对类A进行大的更改,例如将其方法设置为私有并让它使用__call()

2 个答案:

答案 0 :(得分:1)

类似的东西:

class A
{
    public function foo()
    {
        return __CLASS__;
    } 
}

class B extends A
{
    public function foo()
    {
        return __CLASS__;
    }
}

$b = new B();

$reflection = new ReflectionObject($b);

$parentReflection = $reflection->getParentClass();

$parentFooReflection = $parentReflection->getMethod('foo');

$data = $parentFooReflection->invoke($b);

echo $data;

请注意,您可以在invoke(object $object, methods argument1, methods argument2...)函数

中的第一个参数后传递方法参数

你可以使用Reflections做很多类似的事情,有关详细信息,请查看链接http://php.net/manual/en/book.reflection.php

答案 1 :(得分:0)

我接受了@ dm03514的建议并跳进Phokito's source。至少,Phokito使用模拟构建器动态生成模拟类定义,扩展基类并覆盖其所有方法,然后使用class MockFactory { public function buildMock( $class ) { $reflection = new \ReflectionClass( $class ); $mockedShortName = $reflection->getShortName(); $mockShortName = "Mock$mockedShortName"; $mockClass = "\\$mockShortName"; if ( ! class_exists($mockClass) ) { $this->declareMockClass( $reflection, $mockShortName, $mockedShortName ); } return new $mockClass; } private function declareMockClass( $reflection, $mockShortName, $mockedShortName ) { $php = []; $mockedNamespace = $reflection->getNamespaceName(); $extends = $reflection->isInterface() ? 'implements' : 'extends'; $php[] = <<<EOT class $mockShortName $extends $mockedNamespace\\$mockedShortName { public function setReturnValue( \$method, \$returnValue ) { \$this->\$method = \$returnValue; } public function getCalls ( \$method ) { \$callsProperty = \$method . "Calls"; return \$this->\$callsProperty; } EOT; foreach ( $reflection->getMethods() as $method ) { $methodName = $method->name; $params = []; foreach ( $method->getParameters() as $i => $parameter ) { if ( $parameter->isArray() ) $type = 'array '; else if ( $parameterClass = $parameter->getClass() ) $type = '\\' . $parameterClass->getName() . ' '; else $type = ''; $params[] = "$type \${$parameter->getName()}"; } $paramString = implode( ',', $params ); $php[] = <<<EOT private \${$methodName}Calls = []; public function $methodName($paramString) { \$this->{$methodName}Calls[] = func_get_args(); return \$this->$methodName; } EOT; } $php[] = '}'; $toEval = implode( "\n\n", $php ); eval( $toEval ); } } 声明定义的类。

这是基于这种方法的基本模拟构建器:

"\xef\xbf\xbdPNG\r\x1a\r\x00\x00\x00\rIHDR\x00\x00\x01\xef\xbf\xbd…"