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


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

четверг, 28 октября 2010 г.

Junit хитрости: Пишем broken test suite generator

Только никому не говорите, но я имею дело с интеграционными тестами, которые в общей сложности запускаются 3 часа. Так случилось, что систему трогать никак нельзя, а значит разделять ее на модуля или вносить какие-то другие изменения - нельзя. Unit тестами и не пахнет. Есть сервис - его и тестируем. Все тормозит, а потому 300 тестов 3 часа. Читать дальше...

Такие исходные условия вносят свои коррективы в Test Driven Development от Кента Бека: все тесты запускаются только перед отправкой кода в реппозиторий + более тесная работа ведется только с неким набором тестов. Все усугубляется тем, что обновления из реппозитория вносят свои погрешности - каждый раз что-то ломается. По этой причине обновление делается вечером, а тесты запускаются на ночь - утром я получаю список поломанных тестов и начинаю выяснять в чем дело (естественно через debug, ибо интеграционные тесты не дают информации где поломалось, они только говорят что что-то в процессе QWE поломалось). Но даже такое использование тестов здоровско спасает, иначе я вообще не контролировал бы ничего.

Итак в чем трудность: работать с jUnit тестами через Eclipse плагин не совсем удобно. Есть там кнопочка "Run Test - Failure First", но если Suite состоит из нескольких TestCase, то в каждом отдельном TestCase вначале запустятся красные тесты, а потом зеленые.


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


Если обратить внимание, то на той же панели jUnit Runner GUI  есть кнопка для работы с историей запусков. Там же есть возможность импортировать результаты в виде xml. Оу!


Сохраним результаты выполнения...


А вот пример внутренней структуры xml с описанием отработанного suite...

Ну что, вперед!...

Немного поковырявшись в xml парсерах я остановился на jDom как самом простом. Качнул либу jdom.jar и написал простенький класс.

package com;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class SuiteBuilder
{

    public static void main(String[] args) throws JDOMException, IOException
    {
        List tests = new ArrayList();

        processTestSuiteNode(tests, loadXml("c:/broken.xml").getRootElement());

        buildSuiteClass(tests);
    }

    private static Document loadXml(String fileName) throws JDOMException, IOException
    {
        SAXBuilder saxBuilder = new SAXBuilder();
        return saxBuilder.build(new File(fileName));
    }

    private static void buildSuiteClass(List tests)
    {
        System.out.println("package com;");
        System.out.println("");
        System.out.println("import junit.framework.Test;");
        System.out.println("import junit.framework.TestSuite;");
        System.out.println("");
        System.out.println("public final class AllBrokenTests {");
        System.out.println("");
        System.out.println("    private AllBrokenTests() {");
        System.out.println("    }");
        System.out.println("");
        System.out.println("    public static Test suite() {");
        System.out.println("        final TestSuite suite = new TestSuite();");
        for ( String test : tests )
        {
            System.out.println("        " + test);
        }
        System.out.println("        return suite;");
        System.out.println("    }");
        System.out.println("}");
    }

    private static void processTestSuiteNode(List tests, Element rootTestSuite)
    {
        List testSuites = rootTestSuite.getChildren("testsuite");
        for ( Element testSuite : testSuites )
        {
            processTestSuiteNode(tests, testSuite);
        }

        List testCases = rootTestSuite.getChildren("testcase");
        for ( Element testCase : testCases )
        {
            processTestCaseNode(tests, testCase);
        }
    }

    private static void processTestCaseNode(List tests, Element testCase)
    {
        if ( testCase.getChild("error") == null && testCase.getChild("failure") == null )
        {
            return;
        }

        tests.add("suite.addTest(new " +
            testCase.getAttributeValue("classname") +
            "(\"" +
            testCase.getAttributeValue("name") +
            "\");");
    }

}

Думаю с загрузкой файлов сам разберешься.

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

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

Отправить комментарий