Тестирование абстрактных классов

144

Как проверить конкретные методы абстрактного класса с помощью PHPUnit?

Я ожидаю, что мне придется создать какой-то объект как часть теста. Тем не менее, я понятия не имею, лучшая практика для этого или если PHPUnit позволяет это.

Mez
источник
10
Возможно, вам следует подумать об изменении принятого ответа.
Джейкоб
1
Может быть, stackoverflow.com/a/2947823/23963 поможет.
Найджел Торн

Ответы:

240

Модульное тестирование абстрактных классов не обязательно означает тестирование интерфейса, поскольку у абстрактных классов могут быть конкретные методы, и эти конкретные методы могут быть протестированы.

При написании некоторого библиотечного кода не редкость иметь определенный базовый класс, который вы ожидаете расширить на уровне приложения. И если вы хотите убедиться, что код библиотеки протестирован, вам нужны средства для UT конкретных методов абстрактных классов.

Лично я использую PHPUnit, и в нем есть так называемые заглушки и фиктивные объекты, чтобы помочь вам тестировать подобные вещи.

Прямо из руководства по PHPUnit :

abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass('AbstractClass');
        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($stub->concreteMethod());
    }
}

Макет объекта даст вам несколько вещей:

  • вы не обязаны иметь конкретную реализацию абстрактного класса, и вместо этого можете использовать заглушку
  • Вы можете вызывать конкретные методы и утверждать, что они работают правильно
  • если конкретный метод опирается на нереализованный (абстрактный) метод, вы можете заглушить возвращаемое значение с помощью метода will () PHPUnit
Виктор Фараздаги
источник
38

Это хороший вопрос. Я тоже искал это.
К счастью, в PHPUnit уже есть getMockForAbstractClass()метод для этого случая, например

protected function setUp()
{
    $stub = $this->getMockForAbstractClass('Some_Abstract_Class');
    $this->_object = $stub;
}

Важный:

Обратите внимание, что для этого требуется PHPUnit> 3.5.4. В предыдущих версиях была ошибка .

Для обновления до последней версии:

sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit
takeshin
источник
Звучит интересно, но вы будете тестировать против макета? Какими будут тесты? IE: расширение макета в тестовом примере и тестирование в расширенном тестовом классе?
stefgosselin
34

Следует отметить, что начиная с PHP 7 добавлена поддержка анонимных классов . Это дает вам дополнительный способ настройки теста для абстрактного класса, который не зависит от функциональности, специфичной для PHPUnit.

class AbstractClassTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var AbstractClass
     */
    private $testedClass;

    public function setUp()
    {
        $this->testedClass = new class extends AbstractClass {

            protected function abstractMethod()
            {
                // Put a barebones implementation here
            }
        };
    }

    // Put your tests here
}
GordonM
источник
4
Спасибо за это! Использование анонимного класса в PHPUnit дало мне большую гибкость при создании различных тестов.
Алиса Уандер
1

Эран, твой метод должен работать, но он идет вразрез с тенденцией написания теста перед реальным кодом.

Я бы предложил написать тесты на желаемую функциональность неабстрактного подкласса рассматриваемого абстрактного класса, затем написать как абстрактный класс, так и реализующий подкласс, и, наконец, запустить тест.

Ваши тесты, очевидно, должны проверять определенные методы абстрактного класса, но всегда через подкласс.


источник
В качестве произвольного ответа я нахожу: у вас есть абстрактный класс 'A', имеющий общий метод 'foo ()'. Этот метод 'foo ()' используется в общем для всех классов 'B' и 'C', оба являются производными от 'A'. Какой класс вы бы выбрали для тестирования 'foo ()'?
user3790897
1

Нельсон ответил неправильно.

Абстрактные классы не требуют, чтобы все их методы были абстрактными.

Реализованные методы - это те, которые нам нужно протестировать.

То, что вы можете сделать, - это создать поддельный класс-заглушку в файле модульного теста, сделать так, чтобы он расширял абстрактный класс и реализовывал только то, что требуется без какой-либо функциональности, конечно, и проверял это.

Приветствия.

skqr
источник
0

Если вы не хотите создавать подкласс абстрактного класса просто для того, чтобы выполнить модульное тестирование методов, которые уже реализованы в абстрактном классе, вы можете попытаться проверить, позволяет ли ваша инфраструктура моделировать абстрактные классы.

hangy
источник