AtLeastOnce layout with a specific value, the rest is not important

The question is in PHP , but applies to any language using the xUnit .

I need a layout that expects 140 calls to the jump method.
I need to check that at least once there is a call with parameter 500 as the parameter.
I don't care if all calls are 500, but I need at least one that gets called from 500.

 $mock = $this->getMock('Trampoline', ['jump']); $mock->expects($this->atLeastOnce()) ->method('jump') ->with($this->equalTo(500)) ->will($this->returnValue(true)); $sportsman->setTramploine($mock); $sportsman->jumpToRandomHeights($times = 140); // this calls Trampoline->jump // I need to verify the sportsman had jumped // to the height of 500 at least once out of the 140 jumps he is performing 

In the current code, the test failed after the first call to jump , because the first call had a value other than 500 , which means that atLestOnce here indicates only that the method should be called, but the wrong one should be called with special value among other calls .


Decision

The missing piece of information used callbacks inside with . Thanks to edorian's answer below, it worked out:

 $testPassed = false; $checkMinHeight = function ($arg) use(&$testPassed) { if($arg === 500) $testPassed = true; // return true for the mock object to consider the input valid return true; } $mock = $this->getMock('Trampoline', ['jump']) ->expects($this->atLeastOnce()) ->method('jump') ->with($checkMinHeight) ->will($this->returnValue(true)); $sportsman->setTramploine($mock); $sportsman->jumpToRandomHeights($times = 1000); // this calls Trampoline->jump // I need to verify the sportsman had jumped // to the height of 500 at least once out of the 1000 jumps he is performing $this->assertTrue($testPassed, "Sportsman was expected to jump 500m at least once"); 
+4
source share
1 answer

You can, but it's best to implement with PHPUnits the mocking API that I could come up with, it still looks pretty creepy. A.

Another way to resolve this issue is a slightly more readable way to create your own subclass of Trampoline and implement it there.

But for the call:

Assuming this class:

 <?php class FancyMocking { function doThing($value) { } } 

and that we have $x calls, and one of them should have $value > 200 :


 <?php class FancyMockingTest extends PHPUnit_Framework_TestCase { public function testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500() { $maxInvocations = 200; $mock = $this->getMock('FancyMocking'); $mock->expects($this->exactly($maxInvocations)) ->method('doThing') ->with($this->callback(function ($value) use ($maxInvocations) { static $invocationCount = 0; static $maxValue = 0; $maxValue = max($value, $maxValue); /* The assertion function will be called twice by PHPUnit due to implementation details, so the *2 is a hack for now */ if (++$invocationCount == $maxInvocations * 2) { $this->assertGreaterThan(200, $maxValue, 'in 500 tries the max value didn\'t to over 200'); } return true; })) ->will($this->returnCallback(function ($value) { return $value >= 200; })); for($i = $maxInvocations - 2; $i; --$i) { $mock->doThing(50); } var_dump($mock->doThing(250)); var_dump($mock->doThing(50)); } } 

This will give:

 PHPUnit 3.7.9 by Sebastian Bergmann. .bool(true) bool(false) Time: 0 seconds, Memory: 2.75Mb OK (1 test, 2 assertions) 

A call value with 250 returns true, and the whole test case works.

If this fails:

To do this, we will var_dump($mock->doThing(250)); on var_dump($mock->doThing(70)); and run it again:

 PHPUnit 3.7.9 by Sebastian Bergmann. Fbool(false) Time: 0 seconds, Memory: 2.75Mb There was 1 failure: 1) FancyMockingTest::testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500 Expectation failed for method name is equal to <string:doThing> when invoked 200 time(s) in 500 tries the max value didn't to over 200 Failed asserting that 70 is greater than 200. .../FancyMockingTest.php:29 FAILURES! Tests: 1, Assertions: 1, Failures: 1. 

A solution that does not use the PHPUnits API will be something like

class FancyMockingFakeImplementation extends FancyMocking , using this instead of the layout and writing custom logic.

+3
source

All Articles