Автоматический перезапуск упавших тестов с помощью Java, Maven, TeamCity, JUnit

В поддержку инициативы - мега склад примеров на github
Создаем примеры по автоматизации вместе, просто присылайте pull requrest
А также инициативы создания at.info code recipes

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

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

Процесс создания такого набора тестов выгдядит следующим образом:

  1. Сначала мы подключаемся к TeamCity, все параметры подключения указаны в соответствующем файле

Если сервер не доступен напрямую, то при подключении дополнительно вызывается метод openSSHTunnel(), который держит туннель открытым до тех пор, пока не закончит работу.
Настройки соединения указываются в файле ssh.tunnel.properties

  1. Читаем все результаты билда.
  2. Фильтруем только упавшие тесты
  3. Добавляем в сюит

Запуск по id сборки не требует дополнительных параметров, если же нужен запуск последнего развалившегося билда, то в параметрах maven необходимо указать -Dbuild.type=<buildTypeId>, в тимсити его можно увидеть в параметрах в поле Build configuration ID

Весь проект можно скачать и посмотреть на нашем мега-складе github https://github.com/atinfo/at.info-knowledge-base/tree/master/programming/java/junit/run_failed_tests

Пусть таких примеров станет больше, присылайте pull request на наш мега-склад примеров.

2 лайка

Просто для информации хочется добавить немного про реализацию такой фичи в TestNG.

Если для выполнения тестов используется фреймворк TestNG, то он после прогона тестов, при наличии упавших тестов, формирует конфиг запуска testng-failed.xml, используя который можно абсолютно без усилий запустить только упавшие тесты, подсунув ему этот конфиг

2 лайка

В junit можно просто перезапустить используя rule с определенной реализацией.

@sidelnikovmike ждем тогда такой пример в наш мега-склад примеров на github :wink:

@sidelnikovmike очень ждем :smile:

а можно по подробнее про этот способ (для TestNG) и если возможно то с примером

В общем-то я описал его во всех подробностях :slight_smile: То есть это настолько же просто, как запустить тесты в обычном режиме.
В документации по TestNG про это также написано скромно, поскольку всё очень просто

java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs testng.xml
java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs test-outputs\testng-failed.xml
1 лайк

Отпишусь как вернусь из отпуска

Меня интересует как эти упавшие тесты подсунуть TeamCity и чтоб он их запустил,
как запустить упавшые тесты из папки ‘test-outputs\testng-failed.xml’ локально у меня нет проблем, как этот файл подсунуть TeamCity, вот вопрос, его нужно подсунуть через сохраненные артифакты после билда или билд билдить в специфической папке, местонахождение которой я заранее знаю, потом подсовывать из этот папки testng-failed.xml файл в другой Build Steps и там запускать, или может есть какой-то крутой плагин для TeamCity или Jenkins

если у кого-то есть такой опыт поделитесь.

То что работает из коробки для TeamCity здесть Incremental testing with TeamCity | The TeamCity Blog мне не подходит

Может быть и не самая оптимальная и лучшая реализация, но вот:

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class RetryRule implements TestRule {

private List<Class> exceptionClasses;
private int attempts = 3;

public RetryRule(Class... exceptionsClasses) {
    this.exceptionClasses = Arrays.asList(exceptionsClasses);
}

public void setAttempts(int attemptsCount) {
    this.attempts = attemptsCount;
}

@Override
public Statement apply(final Statement base, final Description description) {
    return new Statement() {
        @Override
        public void evaluate() throws Throwable {
            Throwable e = null;
            for (int i = 0; i <= attempts; i++) {
                try {
                    base.evaluate();
                    return;
                } catch (Throwable t) {
                    e = t;
                    if (!isContainsThrowable(e)) {
                        throw e;
                    } else {
                        if(i <= attempts - 1) {
                            logError(e);
                        }
                    }
                }
            }
            throw e;
        }
    };
}

private void logError(Throwable e) {
    e.printStackTrace();
}

private boolean isContainsThrowable(Throwable t) {
    for (Class clazz : exceptionClasses) {
        if (t.getClass().equals(clazz)) {
            return true;
        }
    }
    return false;
}

}

Для своих целей делал logError (у меня там делается несколько вещей, кроме обычного вывода стектрейса). Следовательно можно немного изменять метод isContainsThrowable. Мне нужно было именно так. Рула работает без нареканий пока :smile: Подключение в тестах : @Rule public RetryRule retryRule = new RetryRule(BlaBlaBlaException.class);

UPD:
собственно в конструктор передаются эксепшны, при возникновении которых тест будет рестартоваться.

1 лайк

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

ну да, не совсем то же. может быть я сразу и не понял.
Надеюсь мой пример тоже окажется полезным :smile:

уже)

Опиши подробно в чём проблема:

  • что хочешь сделать
  • что пробовал
  • как не работает

В отдельной теме (т.к. эта тема всё-таки база знаний)