首页 文章

在PHPUnit中模拟对象来模拟静态方法调用?

提问于
浏览
4

我试图测试一个管理数据库中数据访问的类(你知道,CRUD,本质上) . 我们正在使用的DB库碰巧有一个API,您首先通过静态调用获取表对象:

function getFoo($id) {
  $MyTableRepresentation = DB_DataObject::factory("mytable");
  $MyTableRepresentation->get($id);
  ... do some stuff
  return $somedata
}

......你明白了 .

我们试图测试这个方法,但是模拟DataObject的东西,以便(a)我们不需要测试的实际数据库连接,(b)我们甚至不需要为测试包含DB_DataObject lib .

但是,在PHPUnit中我似乎无法获得$ this-> getMock()来正确设置静态调用 . 我有...

$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));

...但测试仍然说未知方法“工厂” . 我知道它正在创建对象,因为在它说它无法找到DB_DataObject之前 . 现在它可以 . 但是,没办法?

我真正想要做的是拥有两个模拟对象,一个用于返回的表对象 . 因此,我不仅要指定工厂是静态调用,而且还要返回我已经设置的一些指定的其他模拟对象 .

我应该提一下,我之前在SimpleTest中做过这个(无法找到代码)并且它工作正常 .

是什么赋予了?

[UPDATE]

我开始意识到它与expect()有关

6 回答

  • 0

    我同意你们两个人最好不要使用静态呼叫 . 但是,我想我忘了提到DB_DataObject是第三方库,静态调用是他们代码使用的最佳实践,而不是我们的代码 . 还有其他方法可以使用涉及直接构造返回对象的对象 . 它只是将那些darned include / require语句留在使用该DB_DO类的任何类文件中 . 这很糟糕,因为如果你同时试图在你的测试中模拟一个同名的类,测试会破坏(或者只是不被隔离) - 至少我认为 .

  • 2

    如果您无法更改库,请更改其访问权限 . 将对DB_DataObject :: factory()的所有调用重构为代码中的实例方法:

    function getFoo($id) {
      $MyTableRepresentation = $this->getTable("mytable");
      $MyTableRepresentation->get($id);
      ... do some stuff
      return $somedata
    }
    
    function getTable($table) {
      return DB_DataObject::factory($table);
    }
    

    现在,您可以使用您正在测试的类的部分模拟,并让getTable()返回一个模拟表对象 .

    function testMyTable() {
      $dao = $this->getMock('MyTableDao', array('getMock'));
      $table = $this->getMock('DB_DataObject', ...);
      $dao->expects($this->any())
          ->method('getTable')
          ->with('mytable')
          ->will($this->returnValue($table));
      $table->expects...
      ...test...
    }
    
  • 1

    这是代码依赖的一个很好的例子 - 设计使得无法注入Mock而不是真正的类 .

    我的第一个建议是尝试重构代码以使用实例而不是静态调用 .

  • 0

    DB_DataObject类中缺少(或不是?)的是在调用工厂方法之前传递准备好的db对象的setter . 这样,您可以在需要时传递模拟或自定义数据库对象(具有相同的接口) .

    在您的测试设置中:

    public function setUp() {
          $mockDb = new MockDb();
          DB_DataObject::setAdapter($mockDb);
     }
    

    factory()方法应该返回模拟的数据库实例 . 如果它尚未集成到您的类中,您可能必须重构factory()方法以使其工作 .

  • 0

    在测试用例中是否需要/包含DB_DataObject的类文件?如果在PHPUnit尝试模拟对象之前该类不存在,则可能会出现这样的错误 .

  • 2

    使用PHPUnit MockFunction扩展和runkit,您还可以模拟静态方法 . 要小心,因为它是猴子修补,因此只应在极端情况下使用 . 不能取代良好的编程习惯 .

    https://github.com/tcz/phpunit-mockfunction

相关问题