Reputation: 817
With PHPUnit, I am testing a sequence of method calls using ->at(), like so:
$mock->expects($this->at(0))->method('execute')->will($this->returnValue('foo'));
$mock->expects($this->at(1))->method('execute')->will($this->returnValue('bar'));
$mock->expects($this->at(2))->method('execute')->will($this->returnValue('baz'));
How can I set up the mock so that, in the above scenario, if execute() is called four or more times, it will immediately fail? I tried this:
$mock->expects($this->at(3))->method('execute')->will($this->throwException(new Exception('Called too many times.')));
But this also fails if execute() is not called four times. It needs to fail immediately, otherwise the system under test will produce errors of its own, which causes the resulting error message to be unclear.
Upvotes: 5
Views: 3180
Reputation: 817
I managed to find a solution in the end. I used a comination of $this->returnCallback()
and passing the PHPUnit matcher to keep track of the invocation count. You can then throw a PHPUnit exception so that you get nice output too:
$matcher = $this->any();
$mock
->expects($matcher)
->method('execute')
->will($this->returnCallback(function() use($matcher) {
switch ($matcher->getInvocationCount())
{
case 0: return 'foo';
case 1: return 'bar';
case 2: return 'baz';
}
throw new PHPUnit_Framework_ExpectationFailedException('Called too many times.');
}))
;
Upvotes: 11
Reputation: 1525
For special cases like this, I typically use something like the following:
public function myMockCallback() {
++$this -> _myCounter;
if( $this -> _myCounter > 3 ) {
// THROW EXCEPTION OR TRIGGER ERROR
}
... THEN YOUR CASE STATEMENT OR IF/ELSE WITH YOUR CHOICE OF RETURN VALUES
}
... INSIDE TEST FUNCTION ....
$mockObject ->expects($this->any())
->method('myMethod')
->will($this->returnCallback( array ($this, 'myMockCallback' )));
Upvotes: 3
Reputation: 419
What about using data providers?
class MyTest extends PHPUnit.... {
/**
* @var const how much till throwing exception
*/
const MAX_EXECUTE_TILL_EXCEPTION = 3;
public function setUp(){}
public function tearDown(){}
/**
* @dataProvider MyExecuteProvider
*/
pulbic function testMyExecuteReturnFalse($data){
$mock = //setup your mock here
//if using "$ret" doesn't work you cant just call another private helper that will decide if you need to
// return value or throwing exception
if (self::MAX_EXECUTE_TILL_EXCEPTION == $data){
$ret = $this->throwException(new Exception('Called too many times.'));
} else {
$ret = $this->returnValue('foo');
}
$mock->expects($this->at($data))->method('execute')->will($ret);
}
public function MyExecuteProvider(){
return array(
0,1,2,3
)
}
}
This is just another idea, and I think that zerkms suggested very good idea as well
Upvotes: 0