模拟在方法中间创建的对象

时间:2017-06-05 11:04:40

标签: php testing phpunit mockery

我知道在Class中间创建method的实例这是一个不好的做法,因为它会使代码难以测试。 但是我无法重构代码,因此我需要找到一种方法来模拟Objectnew被测试中创建的method

二手框架:PHPUnitMockeryWP_Mock

示例:我需要在类get_second_string()

的实例中模拟ExternalClass方法
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');
    }
}

3 个答案:

答案 0 :(得分:1)

如果您真的没有机会重构代码或进行适当的集成测试,您可能需要查看https://github.com/php-test-helpers/php-test-helpers#intercepting-object-creationhttps://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-'));
    }
}

答案 1 :(得分:0)

恕我直言,没有办法做这样的事情。您应该将对象作为参数传递给方法。

答案 2 :(得分:0)

你无法模拟整个对象。 但是使用phpunit你可以做这样的事情:

 $f = $this->getMockBuilder(<your_class>)->disableOriginalConstructor()
      ->setMethods(array(
          <mocked_method_1>, <mocked_method_2>
       ))->getMock();

这样,新创建的对象省略了构造函数,并指定了哪种方法将正常运行以及您模拟哪些方法。

在测试中,您可以指定方法将返回的内容,如下所示:

$f->method(<mocked_method_1>)->willReturn(<dummy_data>);

使用它,你不会以任何方式测试模拟对象,但可以测试创建对象的方法..