Кастомная обработка jUnit тестов в TeamCity

в 10:12, , рубрики: junit, teamcity, test automation, testing, тестирование, метки: , , ,

TeamCity поддерживает jUnit «на лету» и особых проблем с выполнением тестов нет. Но стандартная поддержка не покрывает все юзкейсы. Например, никогда нельзя быть уверенным, в какой очередности пройдут тесты. Кроме того, есть другие вариации тестовой архитектуры, которые просто невозможно сделать дефолтными средствами jUnit. Например, определение в рантайме, какие тесты нужно запускать, а какие нет. Причем с выводом в отчетах в TeamCity без проигнорированных тестов.

Это иногда действительно смущает. У меня была ситуация, когда были написаны тесты для тестирования веб-сервера и все работало хорошо. Но как-то сам веб-сервер упал (в тестах не было логики по остановке / запуску веб-сервера), но часть тестов в репортах TeamCity были отмечены как успешные (так отмечаются в списке все проигнорированные). Естественно заказчик сказал «What the...».
Вроде бы ничего сложного — использовать сервисные сообщения TeamCity. Но в TeamCity есть официальная бага, при которой сервисные сообщения в TestOutput не читаются.

1. Вывод сообщений

Для удобства создадим кастомный класс для вывода сообщений. Чтобы передать команду в TeamCity, необходимо отправить ее в стандартный out поток.

public class Log
{
    public static void logTCTestSuiteStart( String message )
    {
        System.out.println( "##teamcity[testSuiteStarted name='" + message + "']" );
    }

    public static void logTCTestSuiteFinished( String message )
    {
        System.out.println( "##teamcity[testSuiteFinished name='" + message + "']" );
    }

    public static void logTCTestStart( String message )
    {
        System.out.println( "##teamcity[testStarted name='" + message + "']" );
    }

    public static void logTCTestFinished( String message )
    {
        System.out.println( "##teamcity[testFinished name='" + message + "']" );
    }

    public static void logTCTestFailed( String message, AssertionError e )
    {
        System.out.println(
            "##teamcity[testFailed name='" + message + "' message='" + e.getMessage() + "']" );
    }
}

Каждая группа тестов должна быть размещена в блоке TestSuite, используя команды 'testSuiteStarted' и 'testSuiteFinished'. Каждый тест должен начинаться с 'testStarted' и заканчиваться с 'testFinished'. Тест будет считаться успешным, если внутри тестового блока не будет команды 'testFailed'.
Более подробно о поддерживаемых сервисных сообщениях в TeamCity можно почитать тут.

2. Простой тест

Теперь мы можем писать тесты так:

public class BasicTests1
{
    BasicTester tester = new BasicTester( {constructor_args} );

    @BeforeClass
    public static void setUp()
    {
        tester.initialize();        
    }

    @Test
    public void orderedTestRun() throws Exception
    {
        Log.logTCTestSuiteStart( "Basic tests" );
        tester.testOne();
        tester.testTwo();
        tester.testThree();
        Log.logTCTestSuiteFinished( "Basic tests" );
    }

    @AfterClass
    public static void tearDown()
    {
        tester.dispose();
    }
}

public class BasicTester 
{
    protected {field1};
    protected {field2};
    protected {field3};

    protected BasicTester( {class_fields} );

    protected void testOne()
    {
        Log.logTCTestStart( "testOne" );

        try
        {
        //test logic
        }
        catch( AssertionError e )
        {
            Log.logTCTestFailed( "testOne", e );
        }

        Log.logTCTestFinished( "testOne" );
    }

    protected void testTwo()
    {
        Log.logTCTestStart( "testTwo" );

        try
        {
        //test logic
        }
        catch( AssertionError e )
        {
            Log.logTCTestFailed( "testTwo", e );
        }

        Log.logTCTestFinished( "testTwo" );
    }

    protected void testThree()
    {
        Log.logTCTestStart( "testThree" );

        try
        {
        //test logic
        }
        catch( AssertionError e )
        {
            Log.logTCTestFailed( "testThree", e );
        }

        Log.logTCTestFinished( "testThree" );
    }      
}

Так как об ошибках мы сообщаем вручную, то есть смысл их перехватывать. Иначе они просто проигнорируются, и TeamCity, не дождавшись команды testFailed, будет считать тест успешным.

3. Ant task

Тут все стандартно. Единственный момент — таск 'junit' должен содержать 'showoutput=«yes»'. Это заставит выводить в стандартный out поток TestsOuput.

4. Настройки билда TeamCity

Так как TeamCity не читает сервисные команды из TestOutput junit`а, нужно вообще отключить распознавание junit тестов. Для этого необходимо открыть build configuration, перейти во вкладку 'Build parameters' и добавить такие параметры:

  • system.teamcity.ant.junit-support.enabled = false
  • system.teamcity.ant.testng-support.enabled = false

Вот и все. Теперь можно извращаться над тестами jUnit в TeamCity как угодно.

Автор: micherr

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js