PHPUnit — Автоматически повторять неудачные тесты X раз?

#php #phpunit #integration-testing

#php #в PHPUnit #интеграционное тестирование #phpunit #интеграция-тестирование

Вопрос:

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

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

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

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

Есть какие-нибудь идеи?

Редактировать: Многие из вас ответили полезными предложениями, чтобы я не делал этого. Я понимаю, спасибо. Однако конкретно то, что я пытаюсь сделать, — это создать набор тестов, который проверяет работу всей системы, включая удаленные серверы. Я понимаю концепцию тестирования определенных частей моего кода с помощью «макетных» ответов извне … но я также лучше сплю по ночам, если часть моих тестов проверяет «полный стек».

Комментарии:

1. Согласно вашей правке: 1. Когда вы говорите «это не ваш хост», вы говорите «это не часть вашей системы». Таким образом, вы не должны включать это в свой тест, потому что вы все равно не можете повлиять на это, и это приведет к фальсификации результатов. 2. Вы бы никогда не создали тест $this->assertEquals(1, rand(0,1)) , не так ли? Вот что происходит, когда вы включаете неопределенные внешние системы.

Ответ №1:

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

Вы можете либо пофантазировать и переопределить testBare() , чтобы проверить наличие аннотации, такой как @retry 5 , выполнить цикл такое количество раз, вызывая parent::testBare() , и проглотить все исключения (или подмножество), кроме последнего.

 public function runBare() {
    // I'll leave this part to you. PHPUnit supplies methods for parsing annotations.
    $retryCount = $this->getNumberOfRetries();
    for ($i = 0; $i < $retryCount; $i  ) {
        try {
            parent::runBare();
            return;
        }
        catch (Exception $e) {
            // last one thrown below
        }
    }
    if ($e) {
        throw $e;
    }
}
  

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

 public function retryTest($count, $test) {
    // just like above without checking the annotation
    ...
        $test();
    ...
}

public function testLogin() {
    $this->retryTest(5, function() {
        $service = new LoginService();
        ...
    });
}
  

Комментарии:

1. Привет, Дэвид, не могли бы вы предоставить более подробную информацию о том, что делать, я должен повторить тестовый пример один раз, если он потерпел неудачу.

2. Спасибо за ответ, Дэвид. Я хочу просто повторить свой тестовый пример, если он получил сбой без использования каких-либо аннотаций. Что я сделал.. Я поместил код функции run bare [как определено в вашем ответе] в свой базовый класс, используя $ retryCount = 2, после этого мне не ясно:(

3. @Deep123 С $retryCount установленным значением константы, все готово. Вышеуказанное runBare с $retryCount = 2 — это все, что вам нужно. Разве ваши методы тестирования не запускаются дважды, если они терпят неудачу сейчас? Опубликуйте полный тестовый пример и базовый класс в Gist или что-то в этом роде.

4. Также обратите внимание, что пример кода также повторяет пропущенные / неполные тесты. Возможно, вы захотите перехватить и повторно запустить эти тесты, чтобы избежать повторных попыток без необходимости.

5. Полное решение, основанное на этом ответе gist.github.com/makasim/989fcaa6da8ff579f7914d973e68280c

Ответ №2:

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

Комментарии:

1. Это верно для модульных тестов, но для автоматизированных приемочных тестов все еще много споров о том, следует ли имитировать внешние сервисы. Мы решили этого не делать, и это может увеличить хрупкость теста. В таких случаях могут быть полезны повторные попытки.

Ответ №3:

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

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

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

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

Комментарии:

1. 1 и важное последнее предложение: «Кажется, это полностью противоречит тому, что должен делать инструмент».

2. Если вы имитируете, например, браузер во внешнем тестировании, вы делаете это неправильно.

Ответ №4:

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

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

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

В противном случае, если вы хотите автоматизировать больше, вы могли бы реализовать PHPUnit_Framework_TestListener, сохранить количество неудачных тестов в ассоциативном массиве и сравнить с тестовыми запусками. Не уверен, насколько выполним этот маршрут, но вы могли бы попробовать.

Ответ №5:

Вы должны быть в состоянии создать утверждение о подключении к БД и реализовать этот тест в других ваших тестах «как» ваше подключение к БД. Внутри этого теста вы можете пробовать столько раз, сколько вам нужно, и возвращать false после X попыток.