5 июня 2010 г.

Автоматическое занесение отчётов в BTS при нахождении автотестами ошибок

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

Суть идеи такова... гонятся авто-тесты в связке JUnit + Selenium RC, которые в случае нахождения ошибки автоматом создают отчёты об ошибка в багтрекере, как Вам? :)

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

Итак.... гоним первый тест
package seleniumtests;

import com.thoughtworks.selenium.*;
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import javax.imageio.ImageIO;

/**
 *
 * @author makeenkov
 */
public class googletest extends SeleneseTestCase {

    public static String what;
    public static String filename;
    String time = new SimpleDateFormat("dd.MM.yyyy_HH.mm.SS").format(new java.util.Date());

    void recordStep(String name) throws AWTException, IOException {
        File screenshot = new File(new File("."), name + ".png");
        Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
        BufferedImage image = new Robot().createScreenCapture(new Rectangle(0, 0, screenDim.width, screenDim.height));
        try {
            ImageIO.write(image, "png", screenshot);
        } catch (IOException ignore) {
            ignore.printStackTrace();
        }
    }

    @Override
    public void setUp() {
        selenium = new DefaultSelenium("0.0.0.0", 4444, "*firefox", "http://www.google.ru/");
        selenium.start();
    }

    public void test() throws Exception {
        System.out.println("Итак начнём: " + time);
        selenium.open("http://www.google.ru/");
        selenium.waitForPageToLoad("30000");
        selenium.selectWindow("Google");
        selenium.type("q", "black and white");
        selenium.click("btnG");
        selenium.waitForPageToLoad("30000");
        if (selenium.isTextPresent("Расходные материалы для лазерных принтеров") && selenium.isTextPresent("blacknwhite.ru") && selenium.isTextPresent("Картриджи по оптовым ценам: 589-49-04 Расходные материалы для лазерных принтеров.")) {
            System.out.println("Сайт в индексе в топ-5");
        } else {
            System.out.println("Сайт выпал из индекса или из топ-5");
            selenium.windowFocus();             //Gives focus to the currently selected window
            selenium.windowMaximize();           //Resize currently selected window to take up the entire screen
            recordStep("индекс_топ-5_" + time);
            what = "Сайт выпал из индекса или из топ-5" + time;
            filename = "индекс_топ-5_" + time + ".png";
        }
        selenium.close();
        selenium.stop();
        System.out.println("Конец: " + new SimpleDateFormat("dd.MM.yyyy HH.mm.SS").format(new java.util.Date()));
    }
}
который в случае ошибки на выходе даёт нам скриншот и переменные содержащие суть ошибки.
Этот класс вызывается из второго класса
package seleniumtests;

import com.thoughtworks.selenium.*;
import junit.framework.Test;
import junit.framework.TestSuite;

/**
 *
 * @author makeenkov
 */
public class obts extends SeleneseTestCase {

//    googletest gt = new googletest();

    @Override
    public void setUp() {
        selenium = new DefaultSelenium("0.0.0.0", 4444, "*firefox", "http://www.bugtrack-online.com/");
        selenium.start();
    }
   
    public void test() throws Exception {
        System.out.println(googletest.what);
        System.out.println(googletest.filename);
        if (googletest.what == null) {
            System.out.println("Сайт в индексе в топ-5");
        } else {
            selenium.open("/login_page.php");
            selenium.waitForPageToLoad("30000");
            selenium.type("username", "йцукен");
            selenium.type("password", "123456");
            selenium.click("//input[@value='Войти в систему']");
            selenium.waitForPageToLoad("30000");
            selenium.click("link=Online Bug Tracking System");
            selenium.waitForPageToLoad("30000");
            if (selenium.isTextPresent("По всем вопросам пишем в этот отчёт...")) {
            } else {
                selenium.select("project_id", "label=Online Bug Tracking System");
                selenium.waitForPageToLoad("30000");
                selenium.click("//input[@value='Переключиться']");
                selenium.waitForPageToLoad("30000");
            }
            selenium.click("link=создать отчет");
            selenium.waitForPageToLoad("30000");
            selenium.select("category", "label=Категории 1");
            selenium.select("reproducibility", "label=всегда");
            selenium.select("severity", "label=значительная");
            selenium.type("summary", "summary="+googletest.what);
            selenium.type("description", "description="+googletest.what);
            selenium.type("file", "D:\\NetBeansProjects\\seleniumtests\\"+googletest.filename);
            selenium.click("//input[@value='Создать отчет']");
            selenium.waitForPageToLoad("30000");
        }
        selenium.close();
        selenium.stop();
    }

    public static Test googletest() {
        return (Test) new TestSuite(googletest.class);
    }

    public static Test obts2() {
        return (Test) new TestSuite(obts.class);
    }

    public static void main(String args[]) {
        junit.textui.TestRunner.run(googletest());
        junit.textui.TestRunner.run(obts2());
    }
}
который в свою очередь вытягивает переменные и скриншот, а дальше создаёт на основе полученных данных отчёт в багтрекинговой системе.

В итоге получаем вот такого вида отчёт в системе отслеживания ошибок

16 комментариев:

  1. С точки зрения реализации - интересно, но вот с точки зрения результативности - сомнительно.
    Решение о баге должен принимать именно человек, на основе логов скрипта.
    Если будет ошибка в скрипте, то будет заведена ошибка в BTS на продукт... (можно еще кучу вариаций придумать)
    Автоматизация это круто, ей богу, но все автоматизировать зачастую просто не нужно.

    ОтветитьУдалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. Это как раз и есть просто вариант реализации теста...
    Хотя, например, для проверки СЕО меток на странице, самое оно, т.к. по сути не может быть сбоя в тесте ибо он прост до нельзя, а значит ошибки будут только в случае реальных дефектов системы! :)

    ОтветитьУдалить
  4. Похвально.

    Вопросы:

    * автотесты всегда бегают четко и ни разу не падают на ровном месте?

    * много ли их уже написано?

    * они написаны с целью заменить ручное тестирование?

    ЗЫ не "Суть идея такова" а "Суть идеи такова".

    ОтветитьУдалить
  5. Спасибо!

    Ответы:

    * я же сказал, это просто размышления, хотя применение несомненно можно найти

    * тестов не много, но в некоторых, которые бегают по редко меняющемуся функционалу скриншотилка нашла применение :)

    * они идут параллельно ручному тестированию когда функционал новый, а затем остаются только авто-тесты

    ЗЫ Алексей, Вы мой верный Ворд :)

    ОтветитьУдалить
  6. Идея реально суперская, что касается применяемости – это зависит от проекта и организации процесса ... но сегодня я услышал очень интересное мнение от одного тестировщика (кстати – работает в Skype) – для него – регистрация бага ручками – это кайф, ради которого он работает и без этого артифакта работа была бы скучнее :-)

    ОтветитьУдалить
  7. Проблема очевидна.

    Если приложение простое, как Mantis, то можно надеяться на то, что тестирование через ГУЙ будет более-менее стабильным.

    Если же приложение хотя бы где-то будет работать по AJAX - ложные срабатывания, вероятнее всего, станут нормой.

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

    Если тест работает криво на моих глазах, я могу понять, произошла ли ошибка, или был найден баг. А если ложное срабатывание будет моментально занесено в трекер, который кто-то еще мониторит - траблы начнутся :)

    ОтветитьУдалить
  8. 2 Заметки латвийского айтишника:
    Тут уж кому, что... кто вообще не хочет связываться с автоматизацией и ему доставляет удовольствие тыкаться с системой, а кому то приятней смотреть на результат прохождения написанных им тестов! :)

    2 Алексей Лупан:
    "пришлось думать, как их побороть." придумали?

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

    ОтветитьУдалить
  9. 2 Алексей Лупан:
    "траблы начнутся :)" - ну если захотеть, то думаю можно организовать всё так, чтобы работало с пользой... ИМХО
    Ну конечно в случае если такой вариант работы тестов впринципе подходит для тестируемого объекта, а если тесты постоянно ложно срабатывают, то тоже радости мало :)

    ОтветитьУдалить
  10. Раздробили тесты на очень маленькие шаги и начали искать места, на которых происходили непредвиденные сложности.

    Но места нашли, а как побороть врага, который непонятно когда появляется, еще не знаем :)

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

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

    ОтветитьУдалить
  11. "резко сделать статью определенного формата, например, или создать нового юзера с определенным возможностями" - ну это же можно считать небольшим смоук-тестами,т.е. пробежка по основному функционалу, почему бы в работающие тесты не включить такие фичи...
    Т.е. у нас, например, авто-тесты ко всему прочему готовят стенды ночью для ручного тестирования, ну собственно как и у Вас, регистрируют клиентов и прочие мелочи. Вот и хотим мы в таких стопроцентно работающих тестах организовать самозанесение :) отчасти для удовольствия собственного ну и для удобства!

    ОтветитьУдалить
  12. Суть идеи интересна - даже спорить не буду :)
    Сказать честно проходили это уже давно :) сделали, запустили, через пару недель нам менеджеры голову чуть не отвернули, когда на выходных что-то с серваком случилось и а базе появилось 300-400 багов - красивых, тепленьких и румяненьких. А все потому что, мы не учли несколько мелких нюансов ,когда стоит открывать багу, а когда нет, :) как проверить её на дубликаты и т.д.

    Технический нюанс:
    Зачем же багу набивать визуально при помощи селена? Более красиво было бы сделать создание багрепорта напрямую в БД.

    ОтветитьУдалить
  13. Еще, посмотрел. При многопоточном запуске будут грабли, т.к. переменные googletest.what и googletest.filename - статические.

    А если быть окончательно занудным, то предложу вам ознакомиться с Java Naming Convention, где черным по белому написано, что имена классов начинаются с БОЛЬШОЙ буквы.

    К чему я все это? А да, примеры нужно тоже продумывать, т.к. могу использовать 1 к 1 менее опытные товарищи...

    ОтветитьУдалить
  14. Этот комментарий был удален автором.

    ОтветитьУдалить
  15. "Технический нюанс:"
    Алексей Б., Вы читаете мои мысли...

    Объясню, во-первых, просто через селен веселей в том плане, что показательней :) ну если запустить именно этот кусок, т.е. видим: ага, нашёл тест баг и опа, сам его заносит :) АВТОМАТИЗАЦИЯ!!!
    Во-вторых, писать в базу - это лично для меня пройденный этап, это я умею, а с селениумом только начинаю ковыряться.

    В-третьих, мне показалось, что SQL-запросы никому не интересны и для блога немного громоздки...

    А в остальном разницы нет, кому как хочется!

    ОтветитьУдалить
  16. Алексей Б., касательно моих косяков по коду, тут да... я совсем так сказать "лошара"...

    ОтветитьУдалить