Как протестировать конкретный метод, вызывающий абстрактный метод с помощью phpunit

У меня есть абстрактный класс с конкретным методом. Поэтому я хочу проверить эти конкретные методы.

Вот мой абстрактный класс:

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 метод. Заставить мой тест всегда терпеть неудачу.

Есть ли способ издеваться над моим абстрактным методом до создания экземпляра? Должен ли я рефакторинг своего абстрактного класса вместо этого?

1

Решение

Я не думаю, что вы можете настроить свой макет так, как вы хотите. Метод конструкции вызывается, когда вы ->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());
}

}
1

Другие решения

Других решений пока нет …