2012-02-17 67 views
11

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

$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

只是为了清楚起见,你想嘲笑一个现有的类,并给你的模拟该类的默认属性,而不真正实例化被嘲笑的类? – Leigh 2012-02-17 15:31:47

+0

@Leigh右键,该类将在框架中进行更深层次的实例化。我嘲笑的这个类是一个抽象模型,我想给它一些'$ fields',这样它就像一个真正的模型一样。我知道PHPUnit通过将代码写入字符串并对其进行评估()来创建模拟。我只是不知道如何在该过程中包含财产声明。 – 2012-02-17 15:34:50

回答

4

如何你觉得使用反射?

$r = new ReflectionClass('MyClass'); 

$props = $r->getDefaultProperties(); 

$mock = new stdClass; 

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

我还没有用过反射一大堆自己,只为了基本的反思。我不确定你是否能够完全模仿使用它的能见度等,但我不明白为什么不,如果你继续写下字符串和eval ing的路线。

编辑:

通过反射功能出于好奇扫描,这是完全可能的完全模仿与虚拟方法的类,实现完全的可见性约束,常量和静态元素在适当情况下,如果你动态地构建在一个字符串类和eval它。

但是看起来这将是一个完整的使命真正完全支持每一种可能性,当它归结为得到正确的数据类型(你需要的代码从例如数组重建数组构造)

最好的运气,如果你走这条路线,它需要更多的脑力比我愿意现在:)不遗余力

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

class Test 
{ 
    private static $privates = 'priv'; 
    protected $protected = 'prot'; 
    public $public = 'pub'; 
} 

$r = new ReflectionClass('Test'); 

$props = $r->getDefaultProperties(); 

$mock = 'class MockTest {'; 

foreach ($props as $prop => $value) { 
    $rProp = $r->getProperty($prop); 


    if ($rProp->isPrivate()) { 
     $mock .= 'private '; 
    } 
    elseif ($rProp->isProtected()) { 
     $mock .= 'protected '; 
    } 
    elseif ($rProp->isPublic()) { 
     $mock .= 'public '; 
    } 

    if ($rProp->isStatic()) { 
     $mock .= 'static '; 
    } 

    $mock .= "$$prop = "; 

    switch (gettype($value)) { 
     case "boolean": 
     case "integer": 
     case "double": 
      $mock .= $value; 
      break; 
     case "string": 
      $mock .= "'$value'"; 
      break; 
/* 
"array" 
"object" 
"resource" 
*/ 
    case "NULL": 
      $mock .= 'null'; 
      break; 
    } 

    $mock .= ';'; 
} 

$mock .= '}'; 

eval($mock); 

var_dump(new MockTest); 
+2

如果可以的话,我会让你两次。感谢您付出的努力..迄今为止我还没有尝试过反射路线,但听起来很有趣。我会尝试一些建议,看看它是如何:) – 2012-02-17 16:22:59

3

我不知道你甚至需要用于测试目的这样做。

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

你的例子证明了这一点:如果你不需要模拟行为(方法),你不需要模拟对象。您需要一个数据夹具来代替该模型用作其数据源。如果如您所说,“依赖注入不是选项”尤其如此。

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

我只是回答了有关数据库测试昨天的问题,你可能会检查出一些详细信息: PHPUnit: How to test database interactions on remote Postgres server?

+0

好点。我想你可以说我正在尝试创建一个动态夹具。我试图通过创建人造模型来测试底层模型框架。我们的模型几乎完全通过受保护的属性进行配置,所以我可以通过简单地使用'protected $ _fields = array('firstname,'lastname');'来创建用户模型。我的目标是通过动态构建假模型来测试底层框架,从而重新创建任何情况。我已经嘲笑数据网关和框架的其余部分..我只需要能够创建动态类与默认道具 – 2012-02-17 16:18:58

+0

阿哈我看到。在这种情况下,通过一切手段,嘲笑这些模型。一般来说,我会将模型放在一个“集合”对象上,该对象将所有属性存储在一个受保护的数组中,并使用魔术'__get'和'__set'来实现'ArrayAccess',以使属性读/写简单。如果你这样做了,你可以抛出你自己的'Model :: load'方法来从键/值数组中填充一个模型。像这样的'load'方法将使用任何你想要的信息填充对象非常简单。此外,如果需要的话,它可以通过验证等子级进行扩展。 – rdlowrey 2012-02-17 16:24:07

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使用你的框架,这应该工作。如果您需要在单个调用框架的过程中创建同一个模拟类的多个实例,则需要使用某种索引机制来增强上述内容。

+0

这是模型测试。我想创建一个虚假的作者和书籍模型并测试他们的关系。当我使用'$ author-> books'时,框架需要实例化一个新的'Book'对象,并读取它的属性来为数据创建种子并确定与Author的关系标准。所以不,我不能真正将我的模拟'Book'对象注入到任何地方,因为它是动态实例化这些对象的框架工作。我只需要将'Author'类用这些属性放入内存中,而不需要在代码中一致地定义该类。 – 2012-02-17 18:41:53

+0

看看我在问题中的最后一个例子。我只需要能够将这些类写入内存而无需在任何地方对其进行硬编码。我想我可以利用PHPUnit中的Mock Object Builder,但我不确定它是否具有这样的功能。 – 2012-02-17 18:44:38

+0

@MikeB - 查看我的更新。这是你想要达到的目标吗? – 2012-02-17 23:01:23