суббота, 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());
    }
}
который в свою очередь вытягивает переменные и скриншот, а дальше создаёт на основе полученных данных отчёт в багтрекинговой системе.

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

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

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

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

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

    Вопросы:

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

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

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

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

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

    Ответы:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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