PHPUnit:现有对象的模拟方法

2022-08-30 19:09:13

PHPUnit's基于给定的类名创建一个新对象,并允许我更改/测试我指定的方法的行为。getMock($classname, $mockmethods)

我渴望不同的东西;它正在改变现有对象的方法的行为 - 而不构造新对象。

这可能吗?如果是,如何?


在考虑这个问题时,我得出的结论是,通过序列化对象,更改序列化字符串以使对象成为扩展旧类和模拟方法的新类的实例,这是可能的。我想要一些代码 - 或者也许在某个地方已经有了这样的代码。


虽然当然可以再次创建要模拟的对象,但在我的测试中这样做太复杂了。因此,如果我真的不是真的必须这样做,我不想这样做。这是一个TYPO3 TSFE实例,在引导过程中设置一次已经足够困难了。


答案 1

我知道这个答案已经很晚了,但我觉得对于这个问题的未来观众来说,现在有一个更简单的解决方案(它有一些缺点,但根据您的需求可以更容易地实现)。Mockery支持用他们所谓的“代理部分模拟”来模拟预先存在的对象。他们说这是针对具有最终方法的类,但在这种情况下可以使用(尽管文档确实警告说它应该是“最后的手段”)。

$existingObjectMock = \Mockery::mock($existingObject);
$existingObjectMock->shouldReceive('someAction')->andReturn('foobar');

它通过创建一个代理对象来执行操作,该代理对象将所有方法调用和属性 get/set 传递给现有对象,除非它们被模拟。

但是,应该注意的是,代理存在一个明显的问题,即任何类型检查或类型检查都失败。但这通常是可以避免的,因为仍然可以传递。仅当需要模拟功能时才应使用 。$existingObject$existingObjectMock


答案 2

并非所有预先存在的代码都可以测试。代码确实需要设计为可测试的。因此,虽然不完全是您所要求的,但您可以重构代码,以便对象的实例化位于单独的方法中,然后模拟该方法以返回所需的内容。

class foo {
  function new_bar($arg) { return new bar($arg); }
  function blah() {
    ...
    $obj = $this->new_bar($arg);
    return $obj->twiddle();
  }
}

然后你可以用

class foo_Test extends PHPUnit_Framework_TestCase {
  function test_blah() {
    $sut = $this->getMock('foo', array('new_bar', 'twiddle'));
    $sut->expects($this->once())->method('new_bar')->will($this->returnSelf());
    $sut->expects($this->once())->method('twiddle')->will($this->returnValue('yes'));
    $this->assertEquals('yes', $sut->blah());
  }
}

推荐