模拟在方法中间创建的对象
Posted
技术标签:
【中文标题】模拟在方法中间创建的对象【英文标题】:Mock an object created in the middle of a method 【发布时间】:2017-11-06 03:28:53 【问题描述】:我知道在method
中间创建Class
的实例是一种不好的做法,因为它会使代码难以测试。
但是我无法重构代码,所以我需要找到一种方法来模拟一个在被测method
中间使用new
创建的Object
。
使用的框架:phpUnit
、Mockery
、WP_Mock
示例:这里我需要从ExternalClass
类的实例中模拟get_second_string()
方法
Class MyClass
function methodUnderTest($string)
$objToMock = new ExternalClass();
$second_string = $objToMock->get_second_string();
$final_string = $string . $second_string;
return $final_string;
Class TestMyClass extends PHPUnit_Framework_TestCase
public function setUp()
public function tearDown()
public function test_methodUnderTest()
$externalObject = $this->getMockBuilder('ExternalClass')
->setMethods(['get_second_string'])
->getMock;
$externalObject->expects($this->once())
->method('get_second_string')
->willReturn(' two');
$testObj = new MyClass();
$this->assertEquals('one two', $testObj->methodUnderTest('one');
【问题讨论】:
不重构代码,你就不走运了。 @MagnusEriksson,您认为重构代码的最佳方式是什么?我看过xmike的回答,但看起来不是很清楚 【参考方案1】:如果你真的没有机会重构代码或做一些适当的集成测试,你可能想看看https://github.com/php-test-helpers/php-test-helpers#intercepting-object-creation和https://github.com/krakjoe/uopz/tree/PHP5
我仍然认为你编写的代码从重构中获得的收益比猴子修补要多得多。
此外,重构不需要非常繁重。你至少可以这样做:
class MyClass
private $externalsFactory;
public function __construct($externalsFactory)
$this->externalsFactory = $externalsFactory;
public function methodUnderTest($str)
$external = $this->externalsFactory->make();
$second_string = $external->get_second_string();
$finalString = $str.$second_string;
return $finalString;
class ExternalsFactory
public function make()
return new ExternalClass();
class ExternalClass
public function get_second_string()
return 'some real stuff may be even from database or whatever else it could be';
class MyClassTest extends PHPUnit_Framework_TestCase
private $factoryMock;
private $myClass;
public function setUp()
$this->factoryMock = $this->getMockBuilder('ExternalsFactory')
->getMock();
$this->myClass = new MyClass($this->factoryMock);
public function testMethodUnderTest()
$extenalMock = $this->createMock('ExternalClass');
$extenalMock->method('get_second_string')
->willReturn('second');
$this->factoryMock->method('make')
->willReturn($extenalMock);
$this->assertSame('first-and-second', $this->myClass->methodUnderTest('first-and-'));
【讨论】:
谢谢你的提示,我想我会通过这两个组件,因为它们已经老了。另一方面,您建议的重构对我来说不是很清楚。我将提供一个更广泛的示例,因此您应该更容易指出所需的更改 我已经根据问题的更新更新了示例。我包含了工厂的虚拟类和ExternalClass
(如您所愿)以使其完全可运行。
非常感谢,想知道创建新类 ExternalsFactory
是否太多【参考方案2】:
恕我直言,没有办法做这样的事情。您应该将对象作为参数传递给方法。
【讨论】:
【参考方案3】:您不能模拟整个对象。 但是使用 phpunit 你可以这样做:
$f = $this->getMockBuilder(<your_class>)->disableOriginalConstructor()
->setMethods(array(
<mocked_method_1>, <mocked_method_2>
))->getMock();
这样,新创建的对象会省略构造函数,您可以指定哪个方法将正常运行以及模拟哪个方法。
在测试中,您可以指定方法将返回的内容,如下所示:
$f->method(<mocked_method_1>)->willReturn(<dummy_data>);
使用它,您将不会以任何方式测试模拟对象,但可以测试创建对象的方法..
【讨论】:
我知道如何模拟一个对象,我需要的是:function methodUnderTest() $objToMock = new someClass(); echo $objToMock->method();
没有机会模拟someClass
的实例,我无法测试methodUnderTest
,原因很明显。
那么您可以进行集成测试(您不需要模拟对象)或创建扩展测试的新类,复制代码并从工厂获取对象(可能很容易模拟) .但是第二种方法是有风险的,当原始代码更改时,您也必须更改测试代码.. 没有办法在测试方法中模拟新创建的类实例以上是关于模拟在方法中间创建的对象的主要内容,如果未能解决你的问题,请参考以下文章
在此模拟对象上找不到模拟方法 shouldRecieve() [关闭]
设计模式代理模式 ( 动态代理 | 模拟 Java 虚拟机生成对应的 代理对象 类 )