我需要使用默认的属性集创建一个模拟对象,以便在实例化时可以在代码库中使用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';
}
编辑:简化示例
答案 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
一起使用的类名,这应该可行。如果您需要在单次调用框架期间能够创建同一个模拟类的多个实例,则需要使用某种索引机制来增强上述实例。