5

質問は にありますが、フレームワークPHPを使用するすべての言語に適用されますxUnit

method への 140 回の呼び出しを期待するモックが必要ですjump。パラメータとして 500 を使用した呼び出しが少なくとも 1 回
ある ことを確認する必要があります。 すべての呼び出しが 500 であるかどうかは気にしませんが、少なくとも 1 つは 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の値が と異なるため、テストは の最初の呼び出しの後に失敗します。これは、メソッドを呼び出す必要があることのみを示しており、他の呼び出しの中で特定の値で呼び出す必要があることを示しているわけではありません 500atLestOnce


解決

不足している情報は、 内でコールバックを使用していたこと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");
4

1 に答える 1

4

できますが、私が思いついた PHPUnits モック API を使用した最適な実装は、依然として非常に不気味に見えます。

これを解決する別の方法は、もう少し読みやすい方法で、独自のサブクラスを作成しTrampolineてそこに実装することです。

しかし、挑戦のために:

このクラスを仮定します:

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

$x呼び出しがあり、そのうちの 1 つに$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));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.

PHPUnits モック API を使用しないソリューションは次のようになります。

class FancyMockingFakeImplementation extends FancyMocking、モックの代わりにそれを使用し、そこにカスタムロジックを記述します。

于 2012-11-25T13:27:49.233 に答える