модульное тестирование и статические методы - PullRequest
43 голосов
/ 11 мая 2011

Читая и изучая модульное тестирование, пытаясь найти смысл в следующем посте , в котором объясняются трудности вызовов статических функций.

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

Init::loadConfig('settings.php');
Init::setErrorHandler(APP_MODE); 
Init::loggingMode(APP_MODE);

// start loading app related objects ..
$app = new App();

// После прочтения поста я теперь стремлюсь к этому ...

$init = new Init();
$init->loadConfig('settings.php');
$init->loggingMode(APP_MODE);
 // etc ...

Но несколько десятков тестов, которые я написал для этого класса, одинаковы. Я ничего не изменил, и они все еще проходят. Я что-то не так делаю?

Автор поста утверждает следующее:

Основная проблема со статическими методами - это процедурный код. Я понятия не имею, как провести модульное тестирование процедурного кода. Модульное тестирование предполагает, что я могу создать экземпляр своего приложения изолированно. Во время создания экземпляра я связываю зависимости с помощью mocks / friendlies, которые заменяют реальные зависимости. При процедурном программировании нечего «связывать», поскольку нет объектов, код и данные разделены.

Теперь из поста я понимаю, что статические методы создают зависимости, но не понимаем интуитивно, почему нельзя проверить возвращаемое значение статического метода так же легко, как обычный метод?

Я буду избегать статических методов, но мне бы хотелось иметь представление о КОГДА статические методы полезны, если они вообще есть. Из этого поста видно, что статические методы примерно такие же злые, как и глобальные переменные, и их следует избегать, насколько это возможно.

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

Ответы [ 3 ]

51 голосов
/ 11 мая 2011

Статические методы сами по себе не сложнее, чем методы экземпляра.Проблема возникает, когда метод - статический или другой - вызывает другие статические методы, потому что вы не можете изолировать тестируемый метод.Вот типичный пример метода, который может быть трудно проверить:

public function findUser($id) {
    Assert::validIdentifier($id);
    Log::debug("Looking for user $id");  // writes to a file
    Database::connect();                 // needs user, password, database info and a database
    return Database::query(...);         // needs a user table with data
}

Что вы можете проверить с помощью этого метода?

  • Передача чего-либо, кроме положительных целых чисел InvalidIdentifierException.
  • Database::query() получает правильный идентификатор.
  • Соответствующий пользователь возвращается, когда найден, null, если нет.

Эти требования просты, но вы также должны настроить ведение журнала, подключиться к базе данных, загрузить ее с данными и т. д. Класс Database должен нести единоличную ответственность за тестирование, которое он может подключать и запрашивать.Класс Log должен сделать то же самое для регистрации.findUser() не должен иметь дело со всем этим, но это должно произойти, потому что это зависит от них.

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

function testFindUserReturnsNullWhenNotFound() {
    $log = $this->getMock('Log');  // ignore all logging calls
    $database = $this->getMock('Database', array('connect', 'query');
    $database->expects($this->once())->method('connect');
    $database->expects($this->once())->method('query')
             ->with('<query string>', 5)
             ->will($this->returnValue(null));
    $dao = new UserDao($log, $database);
    self::assertNull($dao->findUser(5));
}

Вышеприведенный тест не пройден, если findUser() пренебрегает вызовом connect(), передает неправильное значение для $id (5 выше) или возвращает что-либо кроме null.Прелесть в том, что база данных не задействована, что делает тест быстрым и надежным, а это означает, что он не потерпит неудачу по причинам, не связанным с тестом, таким как сбой сети или неверные образцы данных.Это позволяет вам сосредоточиться на том, что действительно имеет значение: функциональности, содержащейся в findUser().

20 голосов
/ 11 мая 2011

Себастьян Бергманн соглашается с Миско Хевери и часто цитирует его:

Модульному тестированию нужны швы, швы, где мы предотвращаем выполнение нормального пути кода и как мы добиваемся изоляции тестируемого класса. Швы работают через полиморфизм, мы переопределяем / реализуем класс / интерфейс, а затем подключаем тестируемый класс по-разному, чтобы получить контроль над потоком выполнения. Со статическими методами нечего переопределять. Да, статические методы легко вызывать, но если статический метод вызывает другой статический метод, невозможно переопределить зависимость вызываемого метода.

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

Вы абсолютно правы, что статика считается вредной . Избегайте их.

Проверьте ссылки для получения дополнительной информации, пожалуйста.

Обновление: обратите внимание, что, хотя статика все еще считается вредной, возможность заглушать и имитировать статические методы была удалена с PHPUnit 4.0

1 голос
/ 11 мая 2011

Я не вижу проблем при тестировании статических методов (по крайней мере, таких, которые не существуют в нестатических методах).

  • Имитация объектов передается в тестируемые классы с помощью внедрения зависимостей.
  • Ложные статические методы могут передаваться тестируемым классам с использованием подходящего автозагрузчика или манипулирования include_path.
  • Позднее статическое связывание имеет дело с методами, вызывающими статические методы втот же класс.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...