PHPUnit mocking - немедленно завершается ошибкой, когда метод вызывается x раз - PullRequest
4 голосов
/ 08 июня 2011

С помощью PHPUnit я тестирую последовательность вызовов методов, используя -> at (), например так:

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

Как настроить макет так, чтобы в приведенном выше сценарии если execute () вызывается четыре или более раз, он сразу завершится ошибкой?Я пробовал это:

$mock->expects($this->at(3))->method('execute')->will($this->throwException(new Exception('Called too many times.')));

Но это также не удается, если execute () не вызывается четыре раза.Он должен немедленно завершиться с ошибкой , в противном случае тестируемая система будет выдавать собственные ошибки, в результате чего полученное сообщение об ошибке будет неясным.

Ответы [ 4 ]

9 голосов
/ 09 июня 2011

В конце концов мне удалось найти решение.Я использовал комбинацию $this->returnCallback() и передачу соответствия PHPUnit для отслеживания количества вызовов.Затем вы можете создать исключение PHPUnit, чтобы получить хороший вывод:

$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.');
    }))
;
3 голосов
/ 08 июня 2011

В особых случаях, подобных этому, я обычно использую что-то вроде следующего:

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' )));
2 голосов
/ 08 июня 2011

Вы можете разделить тест на 2 зависимые методы, используя @ зависит от аннотации.

В этом случае ваш первый тест только проверяет, что есть точное выполнение 3 методова во-вторых - другая логика.

0 голосов
/ 08 июня 2011

Как насчет использования поставщиков данных ?

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
        )
    }
}

Это просто еще одна идея, и я думаю, что zerkms также предложил очень хорошую идею

...