1

I have a method (let's call it method2) that calls another method (let's call it method1) multiple times but with different arguments.

Here is the class, MyClass.php:

<?php

class MyClass
{

    public function method1($arg)
    {
        return 'arg was ' . $arg;
    }

    public function method2()
    {
        // Calling the first time with 'one'
        $this->method1('one');

        // Calling other functions
        // ...

        // Calling the first time with 'two'
        $this->method1('two');
    }
}

When testing, I create a stub for method1 in order to control how / when it was called and what it returned. In my test for method2, I follow the order with which the code is executed inside method2.

Here is the test class, MyClassTest.php:

<?php

require_once 'MyClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{

    /** @test */
    public function method2_was_called_successfully_with_one_and_then_two()
    {
        $myClassMock = $this->getMockBuilder('MyClass')
                            ->setMethods(['method1'])
                            ->getMock();

        $myClassMock->expects($this->once())
                    ->method('method1')
                    ->with($this->stringContains('one', true))
                    ->will($this->returnValue('arg was one'));

        // Testing code for the code between the two calls
        // ...

        $myClassMock->expects($this->once())
                    ->method('method1')
                    ->with($this->stringContains('two', true))
                    ->will($this->returnValue('arg was two'));

        $myClassMock->method2();
    }
}

In my test, it looks like PHPUnit does not follow this order and gets stuck with the last (second in this case) call of method1:

There was 1 failure:

1) MyClassTest::method2_was_called_successfully_with_one_and_then_two Expectation failed for method name is equal to when invoked 1 time(s) Parameter 0 for invocation MyClass::method1('one') does not match expected value. Failed asserting that 'one' contains "two".

/path/to/the/files/MyClass.php:14 /path/to/the/files/MyClassTest.php:28

FAILURES! Tests: 1, Assertions: 0, Failures: 1.

Any idea on what the basic thing is that I am missing / doing wrong here?

1 Answer 1

2

You must use at() instead of once() when configuring the mock:

    $myClassMock = $this->getMockBuilder('MyClass')
                        ->setMethods(['method1'])
                        ->getMock();

    $myClassMock->expects($this->at(0))
                ->method('method1')
                ->with($this->stringContains('one', true))
                ->will($this->returnValue('arg was one'));

    $myClassMock->expects($this->at(1))
                ->method('method1')
                ->with($this->stringContains('two', true))
                ->will($this->returnValue('arg was two'));


    // Testing code
    // ....
    // ....

On a side note, it looks weird to me configuring the mock after some testing code is already executed. The usual pattern is configure all the calls that mocks should receive at the start of the test. Then exercise the SUT and check that all the calls were made (normally this last step is automatic).

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! Could you edit your answer with the correct order please? This way I can also use the opportunity to correct a bad habit... :)
edited the answer, just put the 'Testing code' comment below the mock configuration.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.