使用默认属性创建模拟对象

时间:2012-02-17 15:23:40

标签: php phpunit

我需要使用默认的属性集创建一个模拟对象,以便在实例化时可以在代码库中使用elseware。

$mock = $this->getMock('MyClass', array(), array(), 'MyClass_Mock');
$mock->prop = 'foobar';

$myclassMock = new get_class($mock);
var_dump($myclassMock->prop); // NULL
// How can I make this dump 'foobar' ?

我正在测试确定,定位和实例化这些类的框架的一部分,因此注入模拟对象会破坏测试的目的。

我不需要模拟任何方法..只需动态创建一个像这样的模拟类:

class MyClass_Mock extends MyClass {
  public $prop = 'foobar';
}

编辑:简化示例

3 个答案:

答案 0 :(得分:4)

您对使用Reflection感觉如何?

$r = new ReflectionClass('MyClass');

$props = $r->getDefaultProperties();

$mock = new stdClass;

foreach ($props as $prop => $value) {
    $mock->$prop = $value;
}

我自己并没有使用过反射,只是为了基本的内省。我不确定你是否能够完全模仿使用它的可见性等,但我不明白为什么不继续沿着写入字符串和eval的路线。

修改

出于好奇心扫描反射函数,完全可以使用伪方法完全模仿类,如果在字符串中动态构建类并{{1},则在适当的情况下实现完全可见性约束,常量和静态元素它。

然而,看起来它将是一个完整的 任务 来真正完全支持每种可能性,因为它确保数据类型正确(您需要重建代码)数组中的数组构造函数(例如)

祝你好运,如果沿着这条路走下去,它需要更多的脑力,而不是我现在想要的更多:)

这里有一些代码,你可以用常量做同样的事情,并以类似的方式创建空方法。

eval

答案 1 :(得分:3)

我不确定你是否 需要 来进行测试。

通常在测试涉及模型访问的代码时,您使用fixture而不是模拟实际模型,因为模型是“哑”数据结构,不会暴露任何需要模拟的功能。

您的示例证实了这一点:如果您不需要模拟行为(方法),则不需要模拟对象。您需要一个数据夹具,而不是模型用作其数据源。如果像你说的那样“依赖注入不是一个选项”,那么尤其如此。

当然,如果您决定要模拟模型,我建议@Leigh的反射解决方案。

我刚回答了一个关于昨天数据库测试的问题,您可以查看更多细节: PHPUnit: How to test database interactions on remote Postgres server?

答案 2 :(得分:1)

我认为问题在于您必须让被测系统(您的框架)能够使用new直接实例化模型对象,并且每个测试都需要以不同方式设置其属性的默认值

如果是这种情况,您可以创建一个简单的基类,以在构造时填充预定义的属性集。下面的解决方案使用PHP 5.3中的late static binding,但是如果没有稍微调整就可以轻松实现相同的结果。

class MockModel
{
    public static $properties;

    public function __construct() {
        if (isset(static::$properties) && is_array(static::$properties)) {
            foreach (static::$properties as $key => $value) {
                $this->$key = $value;
            }
        }
    }
}

class MockBook extends MockModel { /* nothing needed */ }

function testBookWithTitle() {
    MockBook::$properties = array(
        'title' => 'To Kill a Mockingbird'
    );
    $book = new MockBook;
    self::assertEquals('To Kill a Mockingbird', $book->title);
}

只要您可以为您的框架提供与new一起使用的类名,这应该可行。如果您需要在单次调用框架期间能够创建同一个模拟类的多个实例,则需要使用某种索引机制来增强上述实例。