Макет метода в том же тестируемом классе

#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 
             ->ge‌​tMock();
  

Комментарии:

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 для него». Класс (или код), который не может быть протестирован модулем, является плохо написанным классом (или кодом).