huncyrus
huncyrus

Reputation: 658

PHPUnit mock using mock

I have two class, i want to test it via PHPUnit. But something i do wrongly at mocking the stuffs. I want to altering a method what called by the first class.

class One {
    private $someVar = null;
    private $abc = null;

    public function Start() {
        if ( null == $this->someVar) {
            $abc = (bool)$this->Helper();
        }

        return $abc;
    }

    public function Helper() {
        return new Two();
    }


}
Class Two {
    public function Check($whateverwhynot) {
        return 1;
    }
}


Class testOne extends PHPUnit_Framework_TestCase {
    public function testStart() {
        $mockedService = $this
            ->getMockBuilder(
                'Two',
                array('Check')
            )
            ->getMock();

        $mockedService
            ->expects($this->once())
            ->method('Check')
            ->with('13')
            ->will($this->returnValue(true));

        $mock = $this
            ->getMockBuilder(
                'One',
                array('Helper'))
            ->disableOriginalConstructor()
            ->getMock();

        $mock
            ->expects($this->once()) 
            ->method('Helper')
            ->will($this->returnValue($mockedService));

        $result = $mock->Start();
        $this->assertFalse($result);
    }
}

And the result is for the $result is NULL, instead of 'true'

If I don't use the assert line, i get an error message:

F

Time: 0 seconds, Memory: 13.00Mb

There was 1 failure:

1) testOne::testStart
Expectation failed for method name is equal to <string:Check> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

Ideas?

Update - Environment: PHP 5.4.3x, PHPUnit 3.7.19 - An important thing: can't modify the original classes (Class One and Class Two)

Upvotes: 0

Views: 739

Answers (2)

Schleis
Schleis

Reputation: 43700

You are creating your mocks wrong. You are using $this->getMockBuilder() which takes only one argument, the name of the class to be mocked. You seem to be conflating it with $this->getMock() that takes multiple.

Change the test to:

public function testStart() {
    $mockedService = $this
        ->getMockBuilder('Two')
        ->setMethods(array('Check'))
        ->getMock();

    $mockedService
        ->expects($this->once())
        ->method('Check')
        ->with('13')
        ->will($this->returnValue(true));

    $mock = $this
        ->getMockBuilder('One')
        ->setMethods(array('Helper'))
        ->disableOriginalConstructor()
        ->getMock();

    $mock
        ->expects($this->once()) 
        ->method('Helper')
        ->will($this->returnValue($mockedService));

    $result = $mock->Start();
    $this->assertFalse($result);
}

This isn't a good test because we are needing mock the class that we are testing but you stated that you can't change the classes at all.

Upvotes: 0

Piotr Olaszewski
Piotr Olaszewski

Reputation: 6204

You can use mocks from ouzo-goodies.

Classes:

class One
{
    private $someVar = null;
    private $abc = null;
    private $two;

    public function __construct(Two $two)
    {
        $this->two = $two;
    }

    public function Start()
    {
        if (null == $this->someVar) {
            $abc = (bool)$this->Helper()->Check(1213);
        }

        return $abc;
    }

    public function Helper()
    {
        return $this->two;
    }
}

In constructor inject object Two, thus you can easily mock this object.

class Two
{
    public function Check($whateverwhynot)
    {
        return 1;
    }
}

And tests:

class OneTest extends PHPUnit_Framework_TestCase
{
    /**
     * @test
     */
    public function shouldCheckStart()
    {
        //given
        $mock = Mock::create('\Two');
        Mock::when($mock)->Check(Mock::any())->thenReturn(true);

        $one = new One($mock);

        //when
        $start = $one->Start();

        //then
        $this->assertTrue($start);
    }
}

Docs for mocks.

Upvotes: 2

Related Questions