模拟在方法中间创建的对象
Mock an object created in the middle of a method
我知道在 method
的中间创建 Class
的实例是一种不好的做法,因为它会使代码难以测试。
但是我无法重构代码,所以我需要找到一种方法来模拟在测试中 method
中间使用 new
创建的 Object
。
使用的框架:PHPUnit
、Mockery
、WP_Mock
示例:这里我需要从 class 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');
}
}
恕我直言,没有办法做这样的事情。您应该将对象作为参数传递给方法。
您不能模拟整个对象。
但是使用 phpunit 你可以做这样的事情:
$f = $this->getMockBuilder(<your_class>)->disableOriginalConstructor()
->setMethods(array(
<mocked_method_1>, <mocked_method_2>
))->getMock();
这样,新创建的对象会忽略构造函数,您可以指定哪个方法将正常运行以及您模拟哪个方法。
在测试中,您可以指定 method/s 将 return 的内容,如下所示:
$f->method(<mocked_method_1>)->willReturn(<dummy_data>);
使用它,您不会以任何方式测试模拟对象,但可以测试创建对象的方法..
如果你真的没有机会重构代码或做一些适当的集成测试,你可能想看看https://github.com/php-test-helpers/php-test-helpers#intercepting-object-creation and 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-'));
}
}
我知道在 method
的中间创建 Class
的实例是一种不好的做法,因为它会使代码难以测试。
但是我无法重构代码,所以我需要找到一种方法来模拟在测试中 method
中间使用 new
创建的 Object
。
使用的框架:PHPUnit
、Mockery
、WP_Mock
示例:这里我需要从 class 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');
}
}
恕我直言,没有办法做这样的事情。您应该将对象作为参数传递给方法。
您不能模拟整个对象。 但是使用 phpunit 你可以做这样的事情:
$f = $this->getMockBuilder(<your_class>)->disableOriginalConstructor()
->setMethods(array(
<mocked_method_1>, <mocked_method_2>
))->getMock();
这样,新创建的对象会忽略构造函数,您可以指定哪个方法将正常运行以及您模拟哪个方法。
在测试中,您可以指定 method/s 将 return 的内容,如下所示:
$f->method(<mocked_method_1>)->willReturn(<dummy_data>);
使用它,您不会以任何方式测试模拟对象,但可以测试创建对象的方法..
如果你真的没有机会重构代码或做一些适当的集成测试,你可能想看看https://github.com/php-test-helpers/php-test-helpers#intercepting-object-creation and 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-'));
}
}