Я пытаюсь создать довольно стандартный модульный тест, где я вызываю метод и утверждаю его ответ, однако метод, который я тестирую, вызывает другой метод внутри того же класса, который делает немного тяжелую работу.
Я хочу издеваться над этим методом, но все равно выполняю метод, который я тестирую как есть, только с измененным значением, возвращенным из вызова другому методу.
Я отбросил этот пример, чтобы сделать его максимально простым.
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. Мне нужен способ издеваться над одним методом, но остальная часть объекта не повреждена.
Вы можете издеваться над классом, который вы тестируете, и указать метод, который вы хотите высмеять.
$mock = $this->getMockBuilder('MyClass') ->setMethods(array('handleValue')) ->getMock(); $mock->expects($this->once()) ->method('handleValue') ->will($this->returnValue(23)) //Whatever value you want to return
Однако ИМО это не лучшая идея для ваших тестов. Тестирование таким образом сделает рефакторинг намного сложнее. Вы указываете реализацию класса, а не поведение, которое должен иметь класс. Если handleValue
выполняет много сложной работы, что затрудняет тестирование, подумайте о том, чтобы переместить логику в отдельный класс и ввести ее в свой класс. Затем вы можете создать макет этого класса и передать его testMethod
. Это даст вам дополнительное преимущество в том, чтобы сделать MyClass
более расширяемым, если handleValue
должен адаптировать свое поведение.
http://www.oodesign.com/strategy-pattern.html
Как правило, вы не должны издеваться над тестируемой системой.
Вы можете указать, какие методы высмеивать (частичный макет) с помощью setMethods()
:
// Let's 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
если вы специально их не установили.