Статьи

Поиск ошибок проводки

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

Тем не менее, модульные тесты используют только логику, а не проводку, соединяющую объекты. Вам нужно только построить граф объектов для запуска приложения — но отсутствующий объект или какой-либо NULL в неправильном месте могут взорвать все по первому запросу пользователя.

«Классические» подходы

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

Например, сквозной тест может использовать Selenium для открытия браузера, публикации чего-либо на форуме и проверки создания новой страницы, содержащей обсуждение. Этот вид тестов необходим, но не скажет вам, где проблема, когда они терпят неудачу; они также выполняются намного медленнее , но они действительно проверяют систему в целом, даже осуществляя процесс развертывания и настройки базы данных.

Дух интеграционных тестов JB Rainsberger — это афера, заключающаяся в том, чтобы максимально ограничить эти тесты, введя контрактные тесты и совместные тесты на уровне единиц. В этом подходе для каждого теста совместной работы, включающего двойной тест, такой как макет или заглушка, создается контрактный тест для проверки правильности ожиданий в его производственной реализации.

Синхронное тестирование совместной работы и контрактов основано на дисциплине, как и во многих частях TDD (если вы не пишете тесты до добавления кода, ничто не может вас спасти.) Конечно, что-то должно все же протестировать всю картину, а некоторые — Необходимы сквозные испытания.

Ошибки проводки

Как проверить правильность проводки? Построение графа объектов является операцией контроллера: даже если сквозные тесты не охватывают весь код (и не должны, в противном случае их будет слишком много), большую часть времени проверки выполняется как подсказки типов:

public function __construct(Collaborator $collaborator) { ...

убедитесь в правильности проводки. Вы не можете передать NULL в этом конструкторе PHP. В Java и других языках аналогичные защитные механизмы можно вызывать по введенным параметрам:

assert collaborator != null;

в то время как соответствие интерфейсу уже обеспечивается статической типизацией.

Есть интересный случай, не охватываемый этими проверками: упущение.

Например, в этом строительном коде:

$object->addListener(new RedListener())
       //->addListener(new GreenListener())
       ->addListener(new BlueListener());

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

Конечно, здесь вызывается addListener (), но обычно он забыт в реальных сценариях.

Отражение на помощь

Мы можем выполнить небольшой статический анализ (фактически полустатический анализ, если этот термин существует), чтобы проверить проводку.

В нашем случае реализация Listener может быть добавлена ​​для прослушивания определенных событий. И генерация события, и его обработка проходят модульное тестирование, и единственное, что остается проверить, — это связь между двумя местами.

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

  1. получить список классов путем включения файлов с помощью glob () и вызова get_declared_classes () (подробнее об этом позже).
  2. Запустите приложение (или просто интересную часть графа объектов).
  3. Проверьте граф объекта в точке подключения, чтобы убедиться, что каждый слушатель в приложении присутствует с одним экземпляром.

Поскольку все слушатели находились в одной структуре данных (массив внутри объекта, который маршрутизирует события), необходимо добавить простой метод для их извлечения.

Вот как выглядит тест:

<?php
$files = glob(APPLICATION . 'modules/*/listener/*.php');
foreach ($files as $file) {
    require_once $file;
}
$listeners = array();
foreach (get_declared_classes() as $className) {
    if (strstr($className, 'Listener')) {
        $listeners[] = $className;
    }
}
$listenersRepository = $someFactory->buildListenersRepositoryAccordingToProductionConfiguration();
$instantiated = $listenersRepository->getListeners();
foreach ($listeners as $listenerClass) {
    // check one of $listenersRepository is an instance of $listenerClass
    $found = false;
    foreach ($instantiated as $present) {
        if ($present instanceof $listenerClass) {
            $found = true;
        }
    }
    $this->assertTrue($found, "$listenerClass is not used in the application. Have you forgot to wire it?");
}

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

Выводы

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

public function method(Event_* $event)

где Event_ * — это класс, похожий на Event_BrokenCar. С помощью объекта ReflectionMethod и ReflectionParameter :: getClass () легко извлечь Event_ * и проверить, что каждый метод Listener подключен для прослушивания требуемого события. Нет подклассов для учета.

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