Метод тестирования phpunit, который вызывает другие методы класса, которые требуют макета
Я пытаюсь создать довольно стандартный unit test, где я вызываю метод и утверждаю его ответ, однако метод, который я тестирую, вызывает другой метод внутри того же класса, который делает немного тяжелый подъем.
Я хочу издеваться над этим методом, но все равно выполняю метод, который я тестирую как есть, только с посмеянным значением, возвращенным из вызова другому методу.
Я проглотил этот пример, чтобы сделать его максимально простым.
class MyClass
{
// I want to test this method, but mock the handleValue method to always return a set value.
public function testMethod($arg)
{
$value = $arg->getValue();
$this->handleValue($value);
}
// This method needs to be mocked to always return a set value.
public function handleValue($value)
{
// Do a bunch of stuff...
$value += 20;
return $value;
}
}
Моя попытка написать тесты.
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testTheTestMethod()
{
// mock the object that is passed in as an arg
$arg = $this->getMockBuilder('SomeEntity')->getMock();
$arg->expects($this->any())
->method('getValue')
->will($this->returnValue(10));
// test handle document()
$myClass = new MyClass();
$result = $myClass->testMethod($arg);
// assert result is the correct
$this->assertEquals($result, 50);
}
}
Я пытался издеваться над объектом MyClass, но когда я это делаю и вызываю testMethod, он всегда возвращает null. Мне нужен способ издеваться над одним методом, но остальная часть объекта не повреждена.
Ответы
Ответ 1
Вы можете издеваться над классом, который вы тестируете, и указать метод, который вы хотите высмеять.
$mock = $this->getMockBuilder('MyClass')
->setMethods(array('handleValue'))
->getMock();
$mock->expects($this->once())
->method('handleValue')
->will($this->returnValue(23)) //Whatever value you want to return
Однако IMO это не лучшая идея для ваших тестов. Тестирование, как это, сделает рефакторинг намного сложнее. Вы указываете реализацию класса, а не поведение, которое должен иметь класс. Если handleValue
выполняет много сложной работы, что затрудняет тестирование, подумайте о том, чтобы переместить логику в отдельный класс и ввести ее в свой класс. Затем вы можете создать макет этого класса и передать его в testMethod
. Это даст вам дополнительное преимущество: сделать MyClass
более расширяемым, если handleValue
необходимо адаптировать его поведение.
http://www.oodesign.com/strategy-pattern.html
Как правило, вы не должны издеваться над тестируемой системой.
Ответ 2
Вы можете указать, какие методы высмеивать (частичный макет) с помощью setMethods()
:
// Let do a `partial mock` of the object. By passing in an array of methods to `setMethods`
// we are telling PHPUnit to only mock the methods we specify, in this case `handleValue()`.
$csc = $this->getMockBuilder('Lightmaker\CloudSearchBundle\Controller\CloudSearchController')
->setConstructorArgs($constructor)
->setMethods(array('handleValue'))
->getMock();
// Tell the `handleValue` method to return 'bla'
$csc->expects($this->any())
->method('handleValue')
->with('bla');
Любые другие методы в классе, не указанном в массиве, который вы даете setMethods()
, будут выполняться как есть. Если вы не используете, используйте setMethods
, все методы вернут NULL
, если вы специально их не установили.