How do I make fun of a method called inside a constructor in a PHP unit test?

I am having a problem with a class testing module that has a method called inside the constructor. I do not understand how to taunt this. Perhaps I should use the setUp method for phpUnit?

I am using the Mockery library. Is there a better tool than this?

class ToTest
{

   function __construct() {

       $this->methodToMock(); // need to mock that for future tests 

   }

   // my methods class

}

Any suggestions would be appreciated.

+4
source share
6 answers

If you find it difficult to create an instance of the class for testing, this is the smell of code that your class does too much or does work in the constructor.

http://misko.hevery.com/code-reviewers-guide/

# 1:

  • , ,
  • ( out )
  • ( )
  • factory

, methodToMock , . , , , , .

, , , . , , .

+5

, , . , , , , .

, , , , , . , . , , - , .

+2

, (methodToMock), Dependency Injection .

:

class ToTest{
    private $svc;

    // Constructor Injection, pass the Service object here
    public function __construct($Service = NULL)
    {
        if(! is_null($Service) )
        {
            if($Service instanceof YourService)
            {
                $this->SetService($Service);
            }
        }
    }

    function SetService(YourService $Service)
    {
        $this->svc = $Service
    }

    function DoSomething($request) {
        $svc    = $this->svc;
        $result = $svc->getResult($request);        // Get Result from Real Service
        return $result;
    }

    function DoSomethingElse($Input) {
         // do stuff
         return $Input;
    }
}

:

class ServiceTest extends PHPUnit_Framework_TestCase
{
    // Simple test for DoSomethingElse to work Properly
    // Could also use dataProvider to send different returnValues, and then check with Asserts.
    public function testDoSomethingElse()
    {
        $TestClass = new YourService();
        $this->assertEquals(1, $TestClass->DoSomethingElse(1));
        $this->assertEquals(2, $TestClass->DoSomethingElse(2));
    }

    public function testDoSomething()
    {
        // Create a mock for the YourService class,
        // only mock the DoSomething() method. Calling DoSomethingElse() will not be processed
        $MockService = $this->getMock('YourService', array('DoSomething'));

        // Set up the expectation for the DoSomething() method 
        $MockService->expects($this->any())
                    ->method('getResult')
                    ->will($this->returnValue('One'));

        // Create Test Object - Pass our Mock as the service
        $TestClass = new ToTest($MockService);
        // Or
        // $TestClass = new ToTest();
        // $TestClass->SetService($MockService);

        // Test DoSomething
        $RequestString = 'Some String since we did not specify it to the Mock';  // Could be checked with the Mock functions
        $this->assertEquals('One', $TestClass->DoSomething($RequestString));
    }
}
+2

, . - ... .

, :

/**
 * ArrayPool constructor.
 * @param array $tasks Things that might be tasks
 */
public function __construct(array $tasks)
{
    foreach ($tasks as $name => $parameters) {
        if ($parameters instanceof TaskInterface) {
            $this->addTask($parameters);
            continue;
        }
        if ($parameters instanceof DescriptionInterface) {
            $this->addTask(new Task($parameters));
            continue;
        }
        $this->addPotentialTask($name, $parameters);
    }
}

->addTask ->addPotentialTask, , .

:

/**
 * @test
 * @covers ::__construct
 * @uses \Foundry\Masonry\Core\Task::__construct
 */
public function testConstruct()
{
    $task = $this->getMockForAbstractClass(TaskInterface::class);
    $description = $this->getMockForAbstractClass(DescriptionInterface::class);
    $namedTask = 'someTask';
    $parameters = [];

    $arrayPool =
        $this
            ->getMockBuilder(ArrayPool::class)
            ->disableOriginalConstructor()
            ->setMethods(['addTask', 'addPotentialTask'])
            ->getMock();

    $arrayPool
        ->expects($this->at(0))
        ->method('addTask')
        ->with($task);
    $arrayPool
        ->expects($this->at(1))
        ->method('addTask')
        ->with($this->isInstanceOf(TaskInterface::class));
    $arrayPool
        ->expects($this->at(2))
        ->method('addPotentialTask')
        ->with($namedTask, $parameters);

    $construct = $this->getObjectMethod($arrayPool, '__construct');
    $construct([
        0=>$task,
        1=>$description,
        $namedTask => $parameters
    ]);
}

getObjectMethod, , :

/**
 * Gets returns a proxy for any method of an object, regardless of scope
 * @param object $object Any object
 * @param string $methodName The name of the method you want to proxy
 * @return \Closure
 */
protected function getObjectMethod($object, $methodName)
{
    if (!is_object($object)) {
        throw new \InvalidArgumentException('Can not get method of non object');
    }
    $reflectionMethod = new \ReflectionMethod($object, $methodName);
    $reflectionMethod->setAccessible(true);
    return function () use ($object, $reflectionMethod) {
        return $reflectionMethod->invokeArgs($object, func_get_args());
    };
}

, , , .

enter image description here

TL; DR:

  • Disble __construct
  • mocks
  • __construct
  • .
+1

, .

0
class ToTest
{
   function __construct(){
       $this->methodToMock(); // need to mock that for future tests 
   }
   // my methods class
    public function methodToMock(){}
}

class ToTestTest{
    /**
     * @test
     * it should do something
     */
    public function it_should_do_something(){
        $ToTest = \Mockery::mock('ToTest')
        ->shouldDeferMissing()
        ->shouldReceive("methodToMock")
        ->andReturn("someStub")
        ->getMock();

        $this->assertEquals($expectation, $ToTest->methodToMock());
    }
}
0

Source: https://habr.com/ru/post/1536560/


All Articles