PHPUnit:现有对象的模拟方法

时间:2011-08-17 06:52:14

标签: php object mocking phpunit

PHPUnit的getMock($classname, $mockmethods)根据给定的类名创建一个新对象,让我更改/测试我指定的方法的行为。

我渴望与众不同;它正在改变现有对象方法的行为 - 无需构建新对象。

这可能吗?如果是,怎么样?


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


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

3 个答案:

答案 0 :(得分:10)

让我首先说:欢迎来到单元测试的黑暗面。

含义:您通常不想这样做,但正如您所解释的那样,您有一个看似有效的用例。

runkit

使用runkit

,您可以轻松地做到这一点,而不是简单但比改变您的应用程序架构更容易。
runkit_method_rename(
    get_class($object), 'methodToMock', 'methodToMock_old'
);
runkit_method_add(
    get_class($object), 'methodToMock', '$param1, $param2', 'return 7;'
);

runkit::method_add

并在测试之后再进行method_remove并重命名。我不知道有任何框架/组件可以帮助您实现这一点,但在UglyTestsBaseTest extends PHPUnit_Framework_TestCase中自行实现并不是那么多。

嗯......

如果你有权访问的是对该对象的引用(如:$x中的$x = new Foo();)我不知道有什么方法可以说:$ x,你现在是键入SomethingElse,指向该对象的所有其他变量也应该更改。

我假设您已经知道testing your privates之类的内容,但它对您没有帮助,因为您无法控制the objects life cycle

php测试助手扩展

  

注意: the Test-Helper extension is superseded https://github.com/krakjoe/uopz

此处可能对您有所帮助的是:Stubbing Hard-Coded Dependencies使用php-test-helpers extension,可以执行拦截对象创建等操作。

这意味着当您的应用程序调用{​​{1}}时,您可以破解PHP以使其$x = new Something();包含$x的实例。

您可以使用PHPUnit Mocking API创建该分类,也可以自己编写。


据我所知,这些是您的选择。如果它值得去那里(或者只是为该项目编写集成/硒测试)是你必须自己解决的问题,因为它在很大程度上取决于你的情况。

答案 1 :(得分:10)

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

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

它的作用是创建一个代理对象,该对象将所有方法调用和属性获取/设置传递给现有对象,除非它们被模拟。

但应该注意的是,代理遭受了任何类型检查或类型提示失败的明显问题。但这通常可以避免,因为$existingObject仍然可以传递。只有在需要模拟功能时才应使用$existingObjectMock

答案 2 :(得分:6)

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

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());
  }
}