#php #unit-testing #mocking #phpunit
#php #модульное тестирование #издевательство #phpunit
Вопрос:
Я хочу создать макет метода в том же классе, который я тестирую.
ClassA {
function hardToTest($arg) {
// difficult to test code
}
function underTest() {
return $this->hardToTest('foo');
}
}
Я думал, что мог бы использовать отражение для этого, но, возможно, это просто признак того, что я должен переместиться hardToTest
в другой объект.
Комментарии:
1. Я использую PHPUnit 5.7, и мне нужно было использовать
createPartialMock
и указать, какие методы нужно подделать.2. Я согласен, что необходимость имитировать метод в том же тестируемом классе — это запах кода, и что метод следует перенести в другой класс.
Ответ №1:
Этот тест будет успешным, если underTest()
пройдет 'foo'
в hardToTest()
. В документации PHPUnit это называется частичным макетом, потому что вы имитируете только некоторые из методов.
ClassATest {
function testUnderTest() {
$mock = $this->getMock('ClassA', ['hardToTest']);
$mock->expects($this->once())
->method('hardToTest')
->with('foo');
$mock->underTest();
}
}
Я согласен с вашими инстинктами, что эта потребность может быть запахом кода, говорящим вам, что этот класс делает слишком много.
PHPUnit 5.4
Поскольку getMock()
он устарел в версии 5.4, используйте getMockBuilder()
вместо этого:.
$mock = $this->getMockBuilder('ClassA')
->setMethods(['hardToTest']) // onlyMethods in 8.4
->getMock();
Комментарии:
1. на самом деле это должен быть
$mock = $this->getMock('ClassA', [ 'hardToTest' ]);
второй параметрgetMock
, который, как ожидается, будет массивом имен методов.2. В некоторых случаях вам нужно будет выполнить частичный макет, например, если один из методов класса выполняет HTTP-запрос. Было бы неплохо смоделировать этот метод и вернуть заглушку для запроса.
3. @julestruong
$this->getMockBuilder(class)->setMethods(['methodName'])->getMock();
4. Согласен с вами, что это запах кода. Всякий раз, когда вам приходится имитировать метод из того же класса, пришло время подумать о перемещении его в независимый класс. Это значительно упростит ваши тесты. Я страдал от этого.
5. вы также можете использовать
$this->getMockBuilder('ClassA')->setConstructorArgs([$arg1, $arg2])
Ответ №2:
К чему я прибегнул, так это к созданию подкласса моей тестируемой системы с отключенным соответствующим методом.
Ответ №3:
В чем именно причина, по которой метод сложно протестировать?
Если метод защищен, и вы действительно хотите его протестировать, тогда вы можете просто расширить свой ClassA и сделать hardToTest($arg)
общедоступным.
Суть в том, что вы не должны изменять класс только потому, что вам нужно написать unittest для него. И вообще, методы private
и protected
не должны тестироваться — вы должны тестировать только общедоступный интерфейс.
Комментарии:
1. Мне нужно протестировать метод, который вызывает другой метод в ТОМ же КЛАССЕ. Я хотел бы иметь возможность имитировать функциональность этого метода во время выполнения. Перенос метода в другой класс не является моим предпочтительным вариантом.
2. да, это довольно близко к идее …. создайте подкласс тестируемой системы и удалите соответствующие методы, которые я не хочу тестировать. спасибо за помощь
3. Плюс, если вы упомянули итог
4. «Суть в том, что вы не должны изменять класс только потому, что вам нужно написать unittest для него». Класс (или код), который не может быть протестирован модулем, является плохо написанным классом (или кодом).