макет atLeastOnce с конкретным значением, остальное не важно

Вопрос в PHP , но применим к любому языку с использованием инфраструктуры xUnit .

Я хочу макет, который ожидает 140 вызовов метода.
Мне нужно проверить, что по крайней мере один раз есть вызов с параметром 500 в качестве параметра.
Мне все равно, если все вызовы 500, но мне нужен хотя бы один, который вызывается с 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 

В текущем коде тест завершился неудачно после первого вызова jump потому что первый вызов имел значение, отличное от 500 , что означает, что atLestOnce здесь указывает только на то, что метод должен вызываться, но не должен его вызывать со специфическим значением среди других звонки.


Решение

Отсутствующая часть информации использовала обратные вызовы внутри with . Благодаря ответу edorian ниже, это то, что разработано:

 $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"); 

Вы можете, но лучше всего реализовать с PHPUnits насмешливый API, который я мог бы придумать, все еще выглядит довольно жутко.

Другим способом решения этого вопроса является немного более читаемый способ создать собственный подкласс Trampoline и реализовать его там.

Но для вызова:

Предполагая этот класс:

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

и что у нас есть вызовы $x и один из них должен иметь $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)); } } 

Это даст:

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

Значение вызова с 250 возвращает true, и весь тестовый пример работает.

Если это не удастся:

Чтобы это произошло, мы меняем var_dump($mock->doThing(250)); to var_dump($mock->doThing(70)); и запустите его снова:

 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. 

Решение, которое не использует API-интерфейс PHPUnits, будет чем-то вроде

class FancyMockingFakeImplementation extends FancyMocking , используя это вместо макета и записывая собственную логику.