PHPUnit的getMock($classname, $mockmethods)
根据给定的类名创建一个新对象,让我更改/测试我指定的方法的行为。
我渴望与众不同;它正在改变现有对象方法的行为 - 无需构建新对象。
这可能吗?如果是,怎么样?
在考虑问题时,我得出的结论是,可以通过序列化对象,更改序列化字符串,让对象成为扩展旧类的新类的实例模拟的方法。 我想要一些代码 - 或者可能已经存在这样的代码。
虽然再次创建被模拟对象肯定是可能的,但在我的测试中做它太复杂了。因此,如果我真的不需要,我不想这样做。它是一个TYPO3 TSFE实例,在引导过程中设置一次就已经足够了。
答案 0 :(得分:10)
让我首先说:欢迎来到单元测试的黑暗面。
含义:您通常不想这样做,但正如您所解释的那样,您有一个看似有效的用例。
使用runkit
,您可以轻松地做到这一点,而不是简单但比改变您的应用程序架构更容易。runkit_method_rename(
get_class($object), 'methodToMock', 'methodToMock_old'
);
runkit_method_add(
get_class($object), 'methodToMock', '$param1, $param2', 'return 7;'
);
并在测试之后再进行method_remove并重命名。我不知道有任何框架/组件可以帮助您实现这一点,但在UglyTestsBaseTest extends PHPUnit_Framework_TestCase
中自行实现并不是那么多。
如果你有权访问的是对该对象的引用(如:$x
中的$x = new Foo();
)我不知道有什么方法可以说:$ x,你现在是键入SomethingElse
,指向该对象的所有其他变量也应该更改。
我假设您已经知道testing your privates之类的内容,但它对您没有帮助,因为您无法控制the objects life cycle。
注意: 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());
}
}