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

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

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

Ответ 1

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

Не так уж редко, когда вы пишете некоторый код библиотеки, чтобы иметь определенный базовый класс, который вы ожидаете расширить на своем прикладном уровне. И если вы хотите убедиться, что код библиотеки протестирован, вам нужны средства 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());
    }
}

Mock object дает вам несколько вещей:

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

Ответ 2

Это хороший вопрос. Я тоже искал это.
К счастью, 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

Ответ 3

Следует отметить, что с 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
}

Ответ 4

Эран, ваш метод должен работать, но это противоречит тенденции написания теста перед фактическим кодом.

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

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

Ответ 5

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

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

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

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

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

Ответ 6

Если вы не хотите подклассифицировать абстрактный класс просто для выполнения unit test методов, которые уже реализованы в абстрактном классе, вы можете попытаться выяснить, позволяет ли ваша структура mock абстрактные классы.