Варианты реорганизации тестовых файлов, в которых один класс зависит от другого класса? - PullRequest
2 голосов
/ 05 сентября 2011

Я реорганизую тестовые наборы унаследованного php-проекта, чтобы тестировать комплекты и столкнуться с проблемой тестирования класса, который зависит от другого класса. Вот исходный макет папки для тестов.

tests/
   |  
   +- models/
   |     | ClassATest.php
   |     | ClassBTest.php
   |     | ...
   +- controllers/
   |     | ...

В настоящее время нет набора тестов. Каждый тест проводится отдельно.

Для начала реорганизации этих тестов я создаю phpunit.xml и группирую тесты по наборам, как показано ниже.

phpunit.xml

<phpunit bootstrap="...">
    <testsuite name="Models">
        <directory>./models</directory>
    </testsuite>
    ...
</phpunit>

Теперь у меня проблемы. У нас есть две модели, ClassA и ClassB, в которых метод ClassB зависит от ClassA.

class ClassA
{
    public function getValue(...)
    {
        ...
    }
}

class ClassB
{
    public function doSomething()
    {
        $a = new ClassA();
        $x = $a->getValue(...);
        // do something with $x
        ...
    }
 }

Здесь, я думаю, мы все видим проблему, когда дело доходит до тестирования. Чтобы смоделировать ClassA :: getValue (), ClassBTest.php имеет (пере) определил ClassA, как показано ниже.

ClassBTest.php

class ClassA
{
    public function getValue(...)
    {
        return 'a mock value';
    }
}

class ClassBTest extends PHPUnit_Framework_TestCase
{
    public function testDoSomething()
    {
        $b = new ClassB();
        // Make an assertion with $b->doSomething()
    }
}

Итак, когда мы тестируем каждую модель отдельно, она работает. Но при запуске в комплекте это вызывает ошибку, поскольку теперь ClassA повторно объявлен в какой-то момент запущенного процесса, а phpunit завершен.

Мой вопрос: как справиться с этой ситуацией? Я попробовал несколько вариантов.

  • Удаление переопределенных классов в тестовых файлах и использование внедрения зависимостей. Этот способ не является предпочтительным, поскольку мы не хотим изменять классы модели для внедрения зависимостей.
  • Использование групповой аннотации для исключения таких тестовых файлов, как ClassBTest.php и запуск исключенных файлов отдельно. Эта опция не удобна.

Можем ли мы настроить phpunit для запуска группы файлов в процессе и другой группы файлов в другом процессе? Может ли PHP загружать и выгружать класс динамически во время выполнения? Если у вас есть другие варианты, пожалуйста, предложите.

Спасибо!

1 Ответ

2 голосов
/ 06 сентября 2011

Может ли PHP динамически загружать и выгружать класс во время выполнения? Если у вас есть другие варианты, пожалуйста, предложите.

Да. Это последнее средство для тестирования кода, который вы не можете реорганизовать. Взгляните на расширение runkit, method_rename . Вы можете переименовать метод orignal, который хотите «макетировать». Создайте макет и переименуйте его после теста.

Более новый php test helpers также предлагает функцию переименования.


В качестве альтернативы вы можете использовать "runInIsolation" / "process-изоляция" . Каждый тестовый пример будет выполняться в другом процессе PHP. Если вы определяете эти классы только в тестовом примере, вы не столкнетесь с «уже определенными» проблемами.


Третий вариант, который не зависит от расширений или порождения большого количества php-процессов, замедляющих ваш набор тестов

Может показаться немного хакишом, но просто предложить все варианты:

class ClassB
{
    public function doSomething()
    {
        $a = new ClassA();
        $x = $a->getValue(...);
        // do something with $x
        ...
    }
 }

без много рефакторинг вы можете сделать следующее:

класс ClassB { публичная функция doSomething () { $ a = $ this-> getClassA (); $ x = $ a-> getValue (...); // сделать что-то с $ x ... }

protected function getClassA() {
    if(!$this->classA) { 
        return new ClassA(); 
    } 
    return $this->classA;
}

public function setClassAForTesting(ClassA $classA) {
    $this->classA = $classA;
}

}

Это будет загромождать вашу кодовую базу "кодом для тестирования", но использование setter injection позволит вам использовать внедрение зависимостей для тестирования, пока ваш производственный код будет работать так же, как и раньше.

Этот метод требует некоторой работы, но, по моему опыту, он гораздо более устойчив, чем использование PHP-расширений для взлома поведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...