У меня есть абстрактный класс с конкретным методом. Поэтому я хочу проверить эти конкретные методы.
Вот мой абстрактный класс:
abstract class File {
private $debug_filename_pattern = 'DELETE_ME_%s.debug';
private $filename;
private $filepath;
abstract public function buildFilename();
public function __construct($debug = false) {
$filename = $this->buildFilename();
if ($debug) {
$filename = sprintf($this->debug_filename_pattern, $filename);
}
$this->filename = $filename;
$this->buildFilepath();
}
private function buildFilepath() {
$this->filepath = ini_get('upload_tmp_dir') . DIRECTORY_SEPARATOR . $this->filename;
}
}
Я прочитал раздел о тестировании абстрактных классов в phpunit документация и я придумал этот тест:
final class FileTest extends \PHPUnit_Framework_TestCase {
public function test() {
$stub = $this->getMockForAbstractClass('MyBundle\File', [true]);
$stub->expects($this->atLeastOnce())
->method('buildFilename')
->withAnyParameters()
->will($this->returnValue('test.log'));
$this->assertEquals('C:\xampp\tmp\DELETE_ME_test.log.debug', $stub->getFilePath());
}
}
Но это не работает. Мой assert всегда возвращает, что это терпит неудачу с этим сообщением об ошибке:
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'C:\xampp\tmp\DELETE_ME_test.log.debug'
+'C:\xampp\tmp\DELETE_ME_.debug'
Я понимаю, что мой фиктивный объект создается, а затем я добавляю макет для buildFilename
метод. Заставить мой тест всегда терпеть неудачу.
Есть ли способ издеваться над моим абстрактным методом до создания экземпляра? Должен ли я рефакторинг своего абстрактного класса вместо этого?
Я не думаю, что вы можете настроить свой макет так, как вы хотите. Метод конструкции вызывается, когда вы ->getMock()
, Тогда вы пытаетесь установить ожидания после факта.
Как правило, я нахожу, что когда что-то становится трудным для тестирования, так как в этом случае это признак того, что есть проблема с дизайном. Я думаю, что проблема в том, что в этом случае вы слишком много делаете в своем конструкторе.
Вы делаете все виды тяжелой работы, чтобы определить путь к файлу для конструкции вашего объекта. Почему бы не изменить это, чтобы это произошло, когда вы звоните getFilePath
, Ваш класс будет выглядеть так:
abstract class File {
private $debug_filename_pattern = 'DELETE_ME_%s.debug';
private $filename;
private $filepath;
protected $debug;
abstract public function buildFilename();
public function __construct($debug = false) {
$this->debug = $debug;
}
private function buildFilepath() {
$filename = $this->buildFilename();
if ($this->debug) {
$filename = sprintf($this->debug_filename_pattern, $filename);
}
$this->filename = $filename;
$this->filepath = ini_get('upload_tmp_dir') . DIRECTORY_SEPARATOR . $this->filename;
}
public function getFilePath() {
if(!this->filepath) {
$this->buildFilepath();
}
return $this->filepath;
}
}
Теперь в вашем тесте, чтобы убедиться, что путь создается только один раз, просто добавьте свое утверждение еще раз.
final class FileTest extends \PHPUnit_Framework_TestCase {
public function test() {
$stub = $this->getMockForAbstractClass('MyBundle\File', [true]);
$stub->expects($this->once())
->method('buildFilename')
->withAnyParameters()
->will($this->returnValue('test.log'));
$this->assertEquals('C:\xampp\tmp\DELETE_ME_test.log.debug', $stub->getFilePath());
$this->assertEquals('C:\xampp\tmp\DELETE_ME_test.log.debug', $stub->getFilePath());
}
}
Других решений пока нет …