PHPUnit - несколько заглушек одного класса - PullRequest
9 голосов
/ 23 марта 2010

Я создаю модульные тесты для класса Foo, и я довольно новичок в модульном тестировании.

Ключевым компонентом моего класса является экземпляр BarCollection, который содержит несколько Bar объектов. Один метод в Foo выполняет итерацию по коллекции и вызывает пару методов для каждого Bar объекта в коллекции. Я хочу использовать объекты-заглушки для генерации серии ответов для моего тестового класса. Как заставить класс-заглушку Bar возвращать разные значения при итерации? Я пытаюсь сделать что-то вроде этого:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar');
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
// populate stubs into `Foo`

// assert results from `Foo->someMethod()`

Таким образом, Foo->someMethod() будет генерировать данные на основе результатов, которые он получает от Bar объектов. Но это дает мне следующую ошибку всякий раз, когда массив длиннее единицы:

There was 1 failure:

1) testMyTest(FooTest) with data set #2 (array(0.5, 0.5))
Expectation failed for method name is equal to <string:GetValue> when invoked zero or more times.
Mocked method does not exist.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(193) : eval()'d code:25

Одна мысль, которую я имел, состояла в том, чтобы использовать ->will($this->returnCallback()) для вызова метода обратного вызова, но я не знаю, как указать обратному вызову, какой объект Bar выполняет вызов (и, следовательно, какой ответ дать).

Другая идея состоит в том, чтобы использовать метод onConsecutiveCalls() или что-то в этом роде, чтобы сказать моей заглушке, чтобы она возвращала 1 в первый раз, 2 во второй раз и т. Д., Но я точно не знаю, как это сделать. Я также обеспокоен тем, что, если мой класс когда-нибудь сделает что-то кроме упорядоченной итерации в коллекции, у меня не будет способа проверить это.

Ответы [ 3 ]

2 голосов
/ 29 марта 2010

Я, к сожалению, не уверен, что вы можете решить свой настоящий вопрос, используя getMock () , но мой опыт работы с getMock () сам по себе невелик.

Единственное, что я могу придумать, не зная вашего класса Bar , это может не помочь: третий параметр getMock () позволяет передавать аргументы конструктора (в виде массива).

Я бы создал свой собственный ложный класс, расширяющий Bar, в качестве помощника по тестированию (причудливое название «просто еще один класс, который, как оказалось, используется только в тестах»), который делает именно то, что мне нравится, и вводит сериюих в ваш Foo объект.Это дает вам все необходимое управление, поскольку вы можете напрямую заменить рассматриваемые методы, которые getMock () не делает.Конечно, это также означает, что вы не тестируете класс Bar в этом тесте, что может не соответствовать вашим ожиданиям - хотя я бы все равно рекомендовал написать отдельный тестовый класс для тестируемого класса, но бывают случаигде это излишне пурист.

$stubs = array();
foreach ($array as $value) {
    $stubs[] = new MyBarTestHelper($value);
}

Кроме того, я искренне удивлен, что вы видите только описанное исключение, когда у вас есть более одного элемента массива.Я заметил, что PHPUnit на самом деле ожидает, что вы объявите любой метод, который вы хотите, чтобы он мог отслеживать, как параметр getMock () , и в противном случае выдает ошибку, поскольку, по сути, внутреннее созданиесобственное расширение класса, оборачивая каждый метод, который вы явно объявляете, логикой, которая позволяет ему определять, был ли он вызван (= добавление имени метода в логический список).

Так что раскрасьте меня наивно (серьезно, я, наверное,Я тестовый новичок, сам), но посмотрите, поможет ли это вам:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar', array('GetValue'));
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
0 голосов
/ 27 марта 2010

Я заметил, что у вас есть лишние скобки после "-> метода ('GetValue')" в вашем коде.Не знаю, скопировали ли вы и вставили это или нет.

0 голосов
/ 25 марта 2010

Это должно удовлетворять требованию возвращать ряд значений по порядку, как он вызывается, если вы знакомы с использованием global. Он не знает, какой Bar вызывается, но если каждый бар вызывается Foo один раз по порядку, тогда заполнение тестовых данных не должно быть слишком сложным.

$barTestData = array('empty',1,2,3,4,5,6);

function barDataCallback(){
    global $barTestData;
    return next($barTestData);
}
...