Intereting Posts
PHP, Memcached работает из командной строки, но не из Интернета Невозможно запросить разрешение на использование Facebook Login API «publish_actions» даже для основного администратора Ошибка «Вызов неопределенного метода stdClass :: delete ()» при попытке удалить строку в Laravel GPS GPRS, как получать и обрабатывать данные в реальном времени PHP – нужно удалить повторяющиеся символы в строке, но хотелось бы включить исключения Htaccess и учетные записи пользователей Переименуйте файл, если он уже существует – система загрузки php Базовое преобразование в python с заданной строкой Невозможно изменить информацию заголовка – заголовки, уже отправленные … WordPress Issue Медленные ответы с использованием API-интерфейса Asana Ajax Не работает в Chrome, Firefox и Opera yii: невозможно получить изображение, используя "через" Паковать данные в двоичную строку в Python 5.4 разыменование на действительный вызов массива 5.3 PHP – Получить bool для эхо-фальшивости, когда false

Mock в PHPUnit – множественная конфигурация одного и того же метода с разными аргументами

Можно ли настроить PHPUnit таким образом?

$context = $this->getMockBuilder('Context') ->getMock(); $context->expects($this->any()) ->method('offsetGet') ->with('Matcher') ->will($this->returnValue(new Matcher())); $context->expects($this->any()) ->method('offsetGet') ->with('Logger') ->will($this->returnValue(new Logger())); 

Я использую PHPUnit 3.5.10, и он терпит неудачу, когда я запрашиваю Matcher, потому что он ожидает аргумент «Logger». Это похоже на то, что второе ожидание переписывает первый, но когда я отказываюсь от макета, все выглядит нормально.

Начиная с PHPUnit 3.6 существует $this->returnValueMap() который может быть использован для возврата разных значений в зависимости от заданных параметров к методу stub.

К сожалению, это невозможно при использовании PHPUnit Mock API по умолчанию.

Я вижу два варианта, которые могут приблизить вас к чему-то вроде этого:

Использование -> at ($ x)

 $context = $this->getMockBuilder('Context') ->getMock(); $context->expects($this->at(0)) ->method('offsetGet') ->with('Matcher') ->will($this->returnValue(new Matcher())); $context->expects($this->at(1)) ->method('offsetGet') ->with('Logger') ->will($this->returnValue(new Logger())); 

Это будет работать нормально, но вы тестируете больше, чем должны (в основном, сначала его вызывают с помощью соединителя, и это деталь реализации).

Также это не удастся, если у вас есть несколько вызовов для каждой из функций!


Принятие обоих параметров и использование returnCallBack

Это больше работает, но работает лучше, поскольку вы не зависите от порядка вызовов:

Рабочий пример:

 <?php class FooTest extends PHPUnit_Framework_TestCase { public function testX() { $context = $this->getMockBuilder('Context') ->getMock(); $context->expects($this->exactly(2)) ->method('offsetGet') ->with($this->logicalOr( $this->equalTo('Matcher'), $this->equalTo('Logger') )) ->will($this->returnCallback( function($param) { var_dump(func_get_args()); // The first arg will be Matcher or Logger // so something like "return new $param" should work here } )); $context->offsetGet("Matcher"); $context->offsetGet("Logger"); } } class Context { public function offsetGet() { echo "org"; } } 

Это приведет к выводу:

 /* $ phpunit footest.php PHPUnit 3.5.11 by Sebastian Bergmann. array(1) { [0]=> string(7) "Matcher" } array(1) { [0]=> string(6) "Logger" } . Time: 0 seconds, Memory: 3.00Mb OK (1 test, 1 assertion) 

Я использовал $this->exactly(2) в матчи, чтобы показать, что это также работает с подсчетом вызовов. Если вам не нужна эта замена для $this->any() , конечно, будет работать.

Вы можете добиться этого с помощью обратного вызова:

 class MockTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provideExpectedInstance */ public function testMockReturnsInstance($expectedInstance) { $context = $this->getMock('Context'); $context->expects($this->any()) ->method('offsetGet') // Accept any of "Matcher" or "Logger" for first argument ->with($this->logicalOr( $this->equalTo('Matcher'), $this->equalTo('Logger') )) // Return what was passed to offsetGet as a new instance ->will($this->returnCallback( function($arg1) { return new $arg1; } )); $this->assertInstanceOf( $expectedInstance, $context->offsetGet($expectedInstance) ); } public function provideExpectedInstance() { return array_chunk(array('Matcher', 'Logger'), 1); } } 

Должен пройти для любых аргументов «Logger» или «Matcher», offsetGet методу offsetGet контекстного offsetGet :

 F:\Work\code\gordon\sandbox>phpunit NewFileTest.php PHPUnit 3.5.13 by Sebastian Bergmann. .. Time: 0 seconds, Memory: 3.25Mb OK (2 tests, 4 assertions) 

Как вы можете видеть, PHPUnit выполнил два теста. Один для каждого значения dataProvider. И в каждом из этих тестов он сделал утверждение для with() и одно для instanceOf , следовательно, четыре утверждения.

Следуя от ответа @edorian и комментариям (@MarijnHuizendveld) относительно обеспечения того, чтобы метод вызывался как с Matcher, так и с Logger, а не просто дважды с помощью Matcher или Logger, вот пример.

 $expectedArguments = array('Matcher', 'Logger'); $context->expects($this->exactly(2)) ->method('offsetGet') ->with($this->logicalOr( $this->equalTo('Matcher'), $this->equalTo('Logger') )) ->will($this->returnCallback( function($param) use (&$expectedArguments){ if(($key = array_search($param, $expectedArguments)) !== false) { // remove called argument from list unset($expectedArguments[$key]); } // The first arg will be Matcher or Logger // so something like "return new $param" should work here } )); // perform actions... // check all arguments removed $this->assertEquals(array(), $expectedArguments, 'Method offsetGet not called with all required arguments'); не $expectedArguments = array('Matcher', 'Logger'); $context->expects($this->exactly(2)) ->method('offsetGet') ->with($this->logicalOr( $this->equalTo('Matcher'), $this->equalTo('Logger') )) ->will($this->returnCallback( function($param) use (&$expectedArguments){ if(($key = array_search($param, $expectedArguments)) !== false) { // remove called argument from list unset($expectedArguments[$key]); } // The first arg will be Matcher or Logger // so something like "return new $param" should work here } )); // perform actions... // check all arguments removed $this->assertEquals(array(), $expectedArguments, 'Method offsetGet not called with all required arguments'); 

Это с PHPUnit 3.7.

Если метод, который вы тестируете, фактически ничего не возвращает, и вам просто нужно проверить, что он вызывается с правильными аргументами, применяется тот же подход. Для этого сценария я также попытался сделать это, используя функцию обратного вызова для $ this-> callback в качестве аргумента для с, а не returnCallback в завещании. Это терпит неудачу, поскольку внутренне phpunit вызывает обратный вызов дважды в процессе проверки обратного вызова совпадения аргументов. Это означает, что подход завершается неудачно, поскольку во втором вызове этот аргумент уже удален из массива ожидаемых аргументов. Я не знаю, почему phpunit называет это дважды (кажется ненужной тратой), и я думаю, вы могли бы обойти это, удалив его только во втором вызове, но я был недостаточно уверен, что это предполагаемое и последовательное поведение phpunit для полагайтесь на происходящее.

Я просто наткнулся на это расширение PHP, чтобы имитировать объекты: https://github.com/etsy/phpunit-extensions/wiki/Mock-Object

Мои 2 цента на эту тему: обратите внимание при использовании в ($ x): это означает, что ожидаемый вызов метода будет вызовом метода ($ x + 1) th на макет-объекте; это не означает, что будет ($ x + 1) -й вызов ожидаемого метода. Это заставило меня потратить немного времени, поэтому я надеюсь, что это не с вами. С уважением к каждому.