15

I am using PHPUnit for my unit tests I am using a mock object to test if a method is called with the correct parameters. This works fine when I just want to do this once.

    $logMock = $this->getMockBuilder('Logger')
            ->disableOriginalConstructor()
            ->getMock();

    //check if it updates the correct record
    $logMock->expects($this->exactly(1))
            ->method('updateLog')
            ->with(456, 'some status');

Now I have the situation that I want to test if the updateLog is called a second time (with different parameters). I don't see how I can do this with the 'with' method.

Does anybody have a suggestion?

4 Answers 4

22

I don't know your mocking framework. Usually you just create another expectation though. I assume that should work with this framework as well.

$logMock->expects($this->exactly(1))
         ->method('updateLog')
         ->with(100, 'something else');

Edit

It seems that the PHPUnit framework doesn't support multiple different expectations on the same method. According to this site you have to use the index functionality.

It would then look like this

$logMock->expects($this->at(0))
        ->method('updateLog')
        ->with(456, 'some status');
$logMock->expects($this->at(1))
         ->method('updateLog')
         ->with(100, 'something else');
$logMock->expects($this->exactly(2))
        ->method('updateLog');
Sign up to request clarification or add additional context in comments.

5 Comments

I am using the PHPUnit internal mock capabilities. Inside my implementation (the method which is being test) the updateLog is called twice, so I can't test method arguments with different expectations.
According to this site you can achieve this by using the call-index functionality. kreamer.org/phpunit-cookbook/1.0/mocks/…
Thanks! The $this->at(index) does the job. Also thanks for the link to the website, some useful info.
Using indexes is sadly not the best solution. Your test will break if you simply reorder the code in your method without changing the actual functionality. But there really is no good solution for this in todays PHPUnit, so this will have to do.
Yes, that's true and is a good point. I don't use PHPUnit, I'm working more in the .net and java section. And there (e.g. moq) you can add more than one expectation per method without the need to specify a strict order.
6

returnCallback

If you can't use withConsecutive(), possibly because you are on an old version of PHPUnit, you have another option with returnCallback.

The returnCallback function is invoked each time your mock method is invoked. This means you can save the arguments that are passed to it for later inspection. For instance:

$actualArgs = array();

$mockDependency->expects($this->any())
    ->method('setOption')
    ->will($this->returnCallback(
        function($option, $value) use (&$actualArgs){
            $actualArgs[] = array($option, $value);
        }
    ));

$serviceUnderTest->executeMethodUnderTest();

$this->assertEquals(
    array(
        array('first arg of first call', 'second arg of first call'),
        array('first arg of second call', 'second arg of second call'),
    ),
    $actualArgs);

Comments

3

As of PHPUnit 4.2, you can use the withConsecutive assertion. Provided that you know the order of the calls. Your mock would look like this:

$logMock->expects($this->exactly(2))
        ->method('updateLog')
        ->withConsecutive(
            array(456, 'some status')
            array(100, 'something else')
        );

Comments

1

The previous answer is correct.

you can find the answer in the PHPUnit manual http://www.phpunit.de/manual/3.6/en/phpunit-book.html#test-doubles.mock-objects.tables.matchers search for matchers. Matchers are the classes returned by the functions any(), never() etc... The one you need is the PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex, returned by the method at()

you can find more by browsing PHPUnit classes (add to the project path and use an IDE like netbeans to jump to them and see what you can use)

Comments

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.