PHPUnit Test Question - Как Unit test мой класс

Я пытаюсь войти в модульное тестирование для очевидных положительных результатов, которые он представляет, и я пытаюсь написать Unit test для класса, который я написал на днях. (Я знаю, что это противоположность TDD, пожалуйста, несите меня)

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

Image по существу обертывает ресурс изображения GD и сохраняет данные вместе с ним. Например, экземпляр Image всегда будет содержать текущее состояние, то есть его новую ширину/высоту при изменении размера, исходные данные изображения и т.д.

Класс Image также содержит методы для <

  • Создание себя из файла, строковых данных или URL-адресов, например. $image->loadFromPath()
  • Создание нового ресурса изображений GD из свойств текущего экземпляра Image, например. для изменения размера изображения для сохранения фоновой прозрачности и т.д.
  • Клонирование ресурса изображений GD для использования в классах манипуляции

То, с чем я борюсь, - это как Unit test этот класс правильно с PHPUnit. Я кое-что прочитал, и у меня есть несколько противоречивых идей о том, как подойти к нему, и я не знаю, какое право. Я,

  • Напишите тест для каждого метода класса. Я где-то читал, что должен проверять каждый метод. Однако некоторые из методов запускают другие (правильно, поэтому я могу добавить), поэтому у вас есть цепочка зависимости. Но я также читал, что каждый Unit test должен быть независимым от другого. Итак, что мне делать, если это так?
  • Напишите каждый тест как маршрут использования класса. Я также где-то читал, что каждый тест должен представлять собой 1 маршрут пути/использования, который вы можете взять с классом. Поэтому, если вы охватите каждое использование, вы в конечном итоге получите полный охват кода.

Итак, какая из них правильная, если таковая имеется?

Ответы

Ответ 1

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

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

Как уже упоминалось, 100% охват тестирования - хорошая цель, но не всегда реалистичная.

Также, в случае GD, будьте осторожны с написанием модульных тестов, которые проверяют функциональность GD (он уже протестирован, вам не нужно тратить время на повторное тестирование). Я бы читать с использованием PHPUnit mocks и stub (и издеваться над файловой системой) в руководстве PHPUnit.

Вот пример примерного примера:

public function testImageIsResized()
{
    $image = new Image();
    $image->loadFromPath('some/path');
    $image->resize(200, 300);
    $this->assertEquals(200, $image->getWidth());
    $this->assertEquals(300, $image->getHeight());
}

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

Ответ 2

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

/**
 * @test
 * @covers MyClass::something()
 * @covers MyClass::_somethingElse()
 */
public function somethingWorksAsExpected()
{
    $this->assertSame($expected, $this->testObject->something());
}

Для личных проектов 100% покрытие кода прекрасное. Тем не менее, я видел переговоры на конференциях, где 100% сомневаются в необходимости. Несмотря на все преимущества, тесты требуют времени для написания, а в проекте с бюджетом может быть достаточно просто проверить 80/20 и оставить некритические низкоприоритетные функции вашего приложения.

Что касается тестирования вашего класса, посмотрите главу "Управление, управляемое поведением" в руководстве PHPUnit. В вашем случае я проверил бы функциональность, описанную в вашем вопросе.

Ответ 3

Стивен Мелроуз сказал:

Однако некоторые из методов запускаются другие (по праву я могу добавить), так что вы то есть цепь зависимости. Но я также читайте, что каждый Unit test должен быть независимым от другого

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