mercredi 14 septembre 2016

PHPUnit: Is it possible to mock a php function to return a mocked object in replacement of the normal object?

I have a constructor in the class someClass that calls a function (initObjectFunc), which its only purpose is to return a newly created instance of Thing class, and places its that object into a property (thingObject) in the constructor.

class someClass {
    public function __construct() {
        $this->thingObject = $this->initObjectFunc();
    }

    function initObjectFunc() {
        return new Thing();
    }
}

class Thing {
    public function __construct() {}

    function funcB() {
        ... funcB normal implementation ...
        return value;
    }
}

I also have a separate function (funcA), also in the someClass class, that uses thingObject to call its inner function (funcB).

 .
 .
 .

function funcA() {
    $data = $this->thingObject->funcB();
    ... other stuff ....
}

In a PHPUnit test class, I had mocked the Thing class and its funcB to return mocked data ('test test test test') when it gets called through the execution of funcA. To connect all the pieces together, I mocked the initObjectFunc to return that mocked Thing class object.

class unitTest extends PHPUnit_Framework_TestCase {

$mockThing = $this->getMockBuilder('Thing')
    ->setMethods(array('funcB'))
    ->getMock()
    ->expects($this->any())
    ->method('funcB')
    ->will($this->returnValue('test test test test'));

$mockSomeClass = $this->getMockBuilder('someClass')
    ->setMethods(array('initObjectFunc'))
    ->getMock()
    ->expects($this->any())
    ->method('initObjectFunc')
    ->will($this->returnValue($mockThing));

}

This setup was created with this execution flow in mind:

$mockSomeClass calls-> funcA() calls-> *mocked*funcB through *mocked*Thing class

Use the $mockSomeClass to call funcA (which, in PHPUnit, should not have been stubbed and should have full functionality), which would call the funcB. However, since I mocked Thing class and its funcB, it should be calling the mocked funcB to return 'test test test test'.

This is not the case when I try to run the tests. It breaks without any errors. Not even the line that says how many tests, assertions, skips, & fails have occurred.

I had also tried this variation, but to no avail:

class someClass {
    public function __construct() {}

    function initObjectFunc() {
        return new Thing();
    }

    function funcA() {
        $thingObject = initObjectFunc();
        $data = $thingObject->funcB();
        ... other stuff ....
    }
}

This variation tries to take the functionality from the constructor and move it into funcA so that the initObjectFunc() gets called and should return a mocked Thing instance before using it to call funcB. This was done under the thought that maybe the someClass constructor gets called before any of the mocks can be made.

I don't know why or how these components are not connecting. Has anyone tried unit testing with this structure and achieved success? I know that a simple refactor of code to become "dependency injection"-friendly would solve this issue, but this is a question just to find out if this path to unit testing works.

Aucun commentaire:

Enregistrer un commentaire