понедельник, 16 декабря 2013 г.

Тестирование верстки 2

Тестирование с помощью fighting layout bugs

Я думаю не многие знают про гугловую open-source либу,для тестирования верстки и различных layouts,а она совсепм даже не плоха.
Взять ее можно вот тут.
Если кратко,что она умеет?
Находит битые ссылки:
  • Сканирует  HTML теги <img> и проверяет src атрибуты.
  • Сканирует CSS  вск атрибуты style и <style> елементы в HTML и все указанные URL в них.
  • Проверяет фавиконки по урлам.
Проверяет необходимый горизонтальный скроллинг

 Можно указать минимально поддерживаемое разрешение экрана для вашей веб страницы
примерно так:


 
FightingLayoutBugs flb = new FightingLayoutBugs(); flb.configure(DetectNeedsHorizontalScrolling.class).setMinimalSupportedScreenResolution(800, 600);

По умолчанию минимальное разрешение 1024 x 768.

  • Так же проверяет текст на слишком маленький контраст.
  • Распознает текст, который очень близко или перекрывает горизонтальный край
  • Распознает текст, который очень близко или перекрывает вертикальный край
И собственно прикрутить,ее очень просто

 
    FirefoxDriver driver = new FirefoxDriver();
    try {
        String testPageUrl = "http://www.test.de/";
        driver.get(testPageUrl);
        WebPage webPage = new WebPage(driver);
        FightingLayoutBugs flb = new FightingLayoutBugs();
        final Collection layoutBugs = flb.findLayoutBugsIn(webPage);
        System.out.println("Found " + layoutBugs.size() + " layout bug(s).");
        for (LayoutBug bug : layoutBugs) {
            System.out.println(bug);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        driver.quit();
    }


 В общем я думаю стоит попробовать.

четверг, 5 декабря 2013 г.

Тестирование верстки

Тестирование верстки

В общем то идея далеко не новая,как быстро и автоматизированно следить за тем,чтобы при новом билде не расползлась верстка в разных браузерах.
Браузеров много,проект большой,мануальные тестировщики все не успеют.
Попробуем автоматизировать.
Для этого подойдет Selenium WD(т.к. у него поддержка кучи браузеров и снятие скриншотов,то которое нам нужно) и всего навсего средства Java чтобы следить за соответствием скринов.
Не буду говорить, как именно нужно снимать и проверять скриншоты,но мне кажется удобно или сделать пачку "эталонных скринов" и новые сравнивать с ними,или лезть на боевой сервер,делать скрин оттуда,а после снимать скрин на qa-стенде.
Второй вариант дольше,но надежнее в плане не устаревани "эталонных скриншотов".
В общем хватит об этом,как это сделать?
Примерно так:
Через WD  снять скрин можно так:

 
   public void makeScreenshot(String methodName) {
        try {
            File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(scrFile, new File("target" + File.separator + "failure_screenshots" +
                    File.separator + methodName+".png"));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    } 
Конечно это не единственный способ для снятия скриншотов,но сойдет.
Вроде есть много java-библиотек для работы с изображениями,но стоящую и для такой не трудной задачи искать действительно тяжело.(я вроде как не нашел)
в общем можно использовать этот класс:


import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * I am gonna pass two images and I am gonna recort only the differences
 * trying to catch if there is a different object or not int the scene
 *
 * @author maikon
 */
public class CheckingDifferentImages {

    public static void checkDifference(String pathToTheFirstScreen, String pathToTheSecond, String nameDifference) {
        BufferedImage im1 = null;
        BufferedImage im2 = null;
        try {
            //loading the two pictures
            //read and load the image
            BufferedImage input = ImageIO.read(new File("target"+File.separator+"failure_screenshots"+File.separator+pathToTheFirstScreen));
            //build an image with the same dimension of the file read
            im1 =
                    new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
            //object create to draw into the bufferedImage
            Graphics2D g2d = im1.createGraphics();
            //draw input into im
            g2d.drawImage(input, 0, 0, null);
            //making all again for the second image

            BufferedImage input2 = ImageIO.read(new File("target"+File.separator+"failure_screenshots"+File.separator+pathToTheSecond));
            //build an image with the same dimension of the file read
            im2 =
                    new BufferedImage(input2.getWidth(), input2.getHeight(), BufferedImage.TYPE_INT_ARGB);
            //object create to draw into the bufferedImage
            Graphics2D g2d2 = im2.createGraphics();
            //draw input into im
            g2d2.drawImage(input2, 0, 0, null);
        } catch (IOException ex) {
            Logger.getLogger(CheckingDifferentImages.class.getName()).log(Level.SEVERE, null, ex);
        }

        showDifference(im1, im2, nameDifference);

    }

    public static void showDifference(BufferedImage im1, BufferedImage im2, String nameDifference) {
        BufferedImage resultImage =
                new BufferedImage(im1.getWidth(), im2.getHeight(), BufferedImage.TYPE_INT_ARGB);

        double THR = 50;
        int area = 0;
        for (int h = 0; h < im1.getHeight(); h++) {
            for (int w = 0; w < im1.getWidth(); w++) {

                int red1 = 0xff & (im1.getRGB(w, h) >> 16);
                int green1 = 0xff & (im1.getRGB(w, h) >> 8);
                int blue1 = 0xff & im1.getRGB(w, h);


                int red2 = 0xff & (im2.getRGB(w, h) >> 16);
                int green2 = 0xff & (im2.getRGB(w, h) >> 8);
                int blue2 = 0xff & im2.getRGB(w, h);

                //euclidian distance to estimate the simil.
                double dist = 0;
                dist = Math.sqrt(Math.pow((double) (red1 - red2), 2.0)
                        + Math.pow((double) (green1 - green2), 2.0)
                        + Math.pow((double) (blue1 - blue2), 2.0));
                if (dist > THR) {
                    resultImage.setRGB(w, h, im2.getRGB(w, h));
                    area++;
                } else {
                    resultImage.setRGB(w, h, 0);
                }
           //2nd option
           /*     if (dist > THR) {
                    resultImage.setRGB(w, h,255);
                    area++;
                } else {
                    resultImage.setRGB(w, h, im1.getRGB(w, h));
                }*/
            } //w
        } //h
        try {
            File fileScreenshot = new File("target" + File.separator + "DifferenceScreens" + File.separator + nameDifference);
            fileScreenshot.getParentFile().mkdirs();
            ImageIO.write(resultImage, "PNG", fileScreenshot);
        } catch (IOException ex) {
            Logger.getLogger(CheckingDifferentImages.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    //end functionn

}

Попробуем что нить простое(не придираться,это для примера)

 
  @Test
    public void testYandex(){
        get("http://www.yandex.ru/");
        makeScreenshot("1");
        get("http://www.yandex.ru/");
        makeScreenshot("2");
        CheckingDifferentImages.checkDifference("1.png","2.png","diff.png");
    }

В результате:
1й скрин
2й скрин
и отличие(если вы заметили то в классе 2 варианта,один закомменитрован) так что:
и 2м способом

Вот и все.

среда, 4 декабря 2013 г.

Security Regression Tests

 Тестированиe безопасности в регрессе

В общем у многих есть регресс сьюты,но мало у кого есть какое-либо тестирование безопасности,я попробую рассказать ,как его можно добавить без особых затрат.
И так,подразумеваем,что у нас есть рабочий регресс который покрывает большую(а лучше полную) часть нашего функционала веб-приложения.
Значит нужно к нему привязать хороший сканер уязвимостей,который поможет находить проблемы на более ранних стадиях разработки.
Советую обратить внимание на инструмент OWASP ZAP, потому что он (как мне кажется) полностью удовлетворяет нашим требованиям.(ну и + OWASP классная команда с активным комьюнити и все такое)
В общем  для начала о нем,что он из себя представляет.Можно погуглить и посмотреть на эти слайды:



А теперь как это работает у меня

Есть сборщик Maven который собирает и запускает наши тесты, и поднимает тулзу Owasp ZAP,которая поднимает свой прокси который я ему задал.
Затем,мы поднимаем браузер с настроенным прокси(указываем прокси ZAP),в котором будут выполняться тесты.
Это позволит нам видеть все запросы и ответы производимые тестами и так же позволит нам отслеживать и проверять все AJAX запросы (которые не всегда могут быть протестированы).
ZAP пассивно просматривает все ответы от веб-приложения и проводит проверку по заданным "рулам".
Пассивное сканирование не изменяет ответы и запросы и следовательно безопасно в использовании на любых площадках.
Сканирование выполняется в фоновом потоке, что исключает снижение скорости работы приложения.

После прогона всего сьюта можно пробежаться пауком по заданному узлу,чтобы найти то,что не прошло через наше прокси.
После чего запускаем активное скнирование
Активное сканирование пытается найти потенциальные уязвимости с использованием известных OWASP аттак.(а их совсем не мало)
Не желательно использовать такой вид тестирования на боевом сервере.
Естественно, активное сканирование находит только определенные виды уязвимостией,логические уязвимости,как контроль доступа, проверены быть не могут и должны проверятся мануальным тестированием.
И да,проводится также проверка на CSRF форм (это проверка на сабмит форм со случайным параметром, используемых для защиты от CSRF атак.)

Вот и все,всем спасибо.

суббота, 16 ноября 2013 г.

Дожидаемся полной загрузки страницы в Selenium WebDriver

Дожидаемся полной загрузки страницы в Selenium WebDriver

Собственно говоря, иногда нужно дождаться полной загрузки страницы прежде чем выполнять какие либо действия.
Или когда не подгружается какой либо ресурс ваши тесты могут висеть бесконечно(что гораздо хуже)
Вообще в интернете советуют использовать либо implicit wait WD,либо завязывать на какой либо элемент с которым мы хотим работать после загрузки.
Можно делать так как советуют,но иногда этого не достаточно.Как в примере с недоступным ресурсом который можно очень долго ждать.
Я предпочитаю примерно такую реализацию:

 
  public void waitForPageLoaded() {
        ExpectedCondition expectation = new
                ExpectedCondition() {
                    public Boolean apply(WebDriver driver) {
                        return WebDriverController.executeScript("return document.readyState").toString().equals("complete");
                    }
                };

        try {
            getInstanceWaitDriver().until(expectation);
        } catch(Throwable error) {
            Assert.fail("Timeout waiting for Page Load Request to complete.");
        }
    }
Можно назвать это хаком,т.к я использую JavaScript, но я так не считаю)
Всем спасибо.

суббота, 10 августа 2013 г.

TestNG Custom Listener

Custom TestNG Listener

Иногда стоит задача,формировать test suite, не в xml а programmatically, например это может быть нужно,когда мы хотим формировать тесты в зависимости от конфигов. Это сделает их гибче и проще настраиваемыми.
Чтобы просто создать и запустить testNG suite нам потребуется всего пара строчек(которые кстати хорошо описаны в документации testNG)

 
        TestNG tng = new TestNG();
            TestListenerAdapter tla = new TestListenerAdapter();
            List suites = new ArrayList();
            tng.addListener(tla);
            XmlSuite suite = new XmlSuite();
            XmlTest testXml = new XmlTest(suite);
            List excludeGroup = new ArrayList ();
            excludeGroup.add(test.getExcludedGroups());
            testXml.setXmlClasses(test.getClassToRun());
            testXml.setExcludedGroups(excludeGroup);
            suites.add(suite);
            tng.setXmlSuites(suites);
            tng.run(); 
            testsNg.run();
Проблема такого подхода,только в том,что сейчас ошибки которые могу быть в тестах совершенно не наглядны,и во время выполнения большого сьюта их никак не увидеть.
Значит нужно добавить свои средства:

 
   TestNG tng = new TestNG();
            TestListenerAdapter tla = new TestListenerAdapter();
            List suites = new ArrayList();
            tng.addListener(tla);
            XmlSuite suite = new XmlSuite();
            XmlTest testXml = new XmlTest(suite);
            List excludeGroup = new ArrayList ();
            excludeGroup.add(test.getExcludedGroups());
            testXml.setXmlClasses(test.getClassToRun());
            testXml.setExcludedGroups(excludeGroup);
            suites.add(suite);
            tng.setXmlSuites(suites);
            tng.run(); 
    List failedTests = Lists.newArrayList();
            failedTests.addAll(tla.getFailedTests());
            failedTests.addAll(tla.getConfigurationFailures());
            if (!failedTests.isEmpty()) {
                String header = String.format("Combined Messages (Total:%d)", failedTests.size());

                List errorMessages = Lists.newArrayList();
                errorMessages.add(header);
                errorMessages.addAll(Lists.transform(failedTests, new Function() {
                    int i = 1;
                    @Override
                    public String apply(ITestResult testResult) {
                        String stackTraceString = Throwables.getStackTraceAsString(testResult.getThrowable());
                        String template = "Message-%d: %n %s";
                        return String.format(template, i++, stackTraceString);
                    }
                }));

                String message = Joiner.on(LINE_SEPARATOR).join(errorMessages);
                throw new AssertionError(message);
            }


С добавлением такого кода,после сьюта выведется вся информация об ошибках со stacktraceом.
Если же нам нужна информация еще во время его прохождения,чтобы не тратить наше драгоценное время на ожидание,то нужно добавить

 
  @AfterMethod
    public void tearDown2(ITestResult result) {

        if (!result.isSuccess()) {
            makeScreenshot(result.getName());
            log.error("Test FAILED! Method:" + result.getName() + ". StackTrace is "+ Throwables.getStackTraceAsString(result.getThrowable()));
        }
    }

Думаю это поможет,всем спасибо.

суббота, 6 июля 2013 г.

SSL Trust Manager

Хак для самоподписных сертификатов.

Код отлично работающий с Http, может совершенно отказываться работать с https,если сертификаты самоподписные.(что чаще всего и есть на тестовых площадках)Для примера, если вы используете в Java класс HttpURLConnection то он выдаст следующее исключение:

 
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: 

Чтобы его избежать, нам нужно отключить проверку сертификатов в https.
Это исключение нам дает класс URL через который обращается HttpURLConnection.Для тестирования достаточно перегрузить trust manager, чтобы он доверял всем сертификатам)

 

// Create a trust manager that does not validate certificate chains 
    TrustManager[] trustAllCerts = new TrustManager[]{ 
        new X509TrustManager() { 
            public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
                return null; 
            } 
            public void checkClientTrusted( 
                java.security.cert.X509Certificate[] certs, String authType) { 
            } 
            public void checkServerTrusted( 
                java.security.cert.X509Certificate[] certs, String authType) { 
            } 
        } 
    }; 
     
    // Install the all-trusting trust manager 
    try { 
        SSLContext sc = SSLContext.getInstance("SSL"); 
        sc.init(null, trustAllCerts, new java.security.SecureRandom()); 
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 
    } catch (Exception e) { 
    } 
     
    // Now you can access an https URL without having the certificate in the truststore 
    try { 
        URL url = new URL("https://hostname/index.html"); 
    } catch (MalformedURLException e) { 
    } 


Больше такой проблемы не появится.

суббота, 25 мая 2013 г.

Hack Xpath

Хак для Selenium WebDriver

Собственно,очень удобно использовать аннотации в WD
@FindBy(id = "idOfYourElement")
WebElement myLocator;
Но иногда требуется достать локатор из используемого элемента.Никакого метода для этого нет,поэтому будем хакать.
Путь это немного костыльно,но иногда необходимо.


  public static String getXPath(WebElement element) {
        String jscript = "function getPathTo(node) {" +
                "  var stack = [];" +
                "  while(node.parentNode !== null) {" +
                "    stack.unshift(node.tagName);" +
                "    node = node.parentNode;" +
                "  }" +
                "  return stack.join('/');" +
                "}" +
                "return getPathTo(arguments[0]);";
        return "//"+(((JavascriptExecutor)driver).executeScript(jscript, element));
    }
Так можно получить локатор по xpath.
Но на мой взгляд,к этому нужно прибегать в крайних случаях.
Всем спасибо.

воскресенье, 14 апреля 2013 г.

DDT in tests

Параметризация тестов или DDT подход

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

XML

Пример с сериализацией и немного инфы по ней(вдруг кто не в курсе)
Сериализация - процесс перевода какой-либо структуры данных в последовательность битов. Обратной к операции сериализации является операция десериализации — восстановление начального состояния структуры данных из битовой последовательности.
Пусть у нас есть простой класс который хранит логин и пароль пользователя:

public class User{
  String login = "name";
  String password = "pass";
}


Используя библиотеку Xstream(не обязательно ее,но мне она показалась самой легковесной)
User infoUser= new User(); 
System.out.println(new XStream().toXml(infoUser)); 
Получим:

 name
 pass

сохранив эту информацию в xml файл мы можем восстановить объект оттуда.



User infoUser2= (User) new XStream().fromXml(new FileInputStream("file.xml"));


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

 Excel

Будем использовать библиотеку  Java Excel API

Самому лень стало создавать подходящий Excel файл,поэтому я его нашел)

Функция этой библиотеки принимает 3 параметра:
1. xlFilePath - Путь к xls файлу
2. sheetName- Имя вашего sheet(На примере DataPool)
3. tableName- И имя таблички из которой будет проводиться выборка.


 private static String[][] getTableArray(String xlsFilePath, String sheetName, String tableName) throws BiffException, IOException {
        String[][] tabArray = null;
            Workbook workbook = Workbook.getWorkbook(new File(xlsFilePath));
            Sheet sheet = workbook.getSheet(sheetName);
            int startRow, startCol, endRow, endCol, ci, cj;
            Cell tableStart = sheet.findCell(tableName);
            startRow = tableStart.getRow();
            startCol = tableStart.getColumn();

            Cell tableEnd = sheet.findCell(tableName, startCol + 1, startRow + 1, 100, 64000, false);

            endRow = tableEnd.getRow();
            endCol = tableEnd.getColumn();
            tabArray = new String[endRow - startRow - 1][endCol - startCol - 1];
            ci = 0;

            for (int i = startRow + 1; i < endRow; i++, ci++) {
                cj = 0;
                for (int j = startCol + 1; j < endCol; j++, cj++) {
                    tabArray[ci][cj] = sheet.getCell(j, i).getContents();
                }
            }
     
        return (tabArray);
    }
А использовать уж как удобно.Просто как пример:
@DataProvider(name = "DP1")
    public Object[][] createData1() throws Exception{
        Object[][] retObjArr=getTableArray("data.xls",
                "DataPool", "imdbTestData1");
        return(retObjArr);
    }
    
    @Test (dataProvider = "DP1")
    public void testDataProviderExample(String ID, 
            String Name, String City) throws Exception { 
    //Что-нибудь делаем с ними тут  } 

Вот и все,всем спасибо.

среда, 30 января 2013 г.

About test plans

О тест планах.

Сам я совсем не сторонник больших тест планов,и увидев такую статью у Pradeep Soundararajan,не мог ей не поделиться.

  • 98% тест планов никогда не обновляются и не поддерживаются в актуальном состоянии, короче говоря, о них просто забывают
  • Первые 5 страниц тест плана содержат историю изменений, которая не интересует даже тех, чьи имена в ней перечислены
  • Раздел описывающий цели тестирования  является самой забавной частью этого документа. Порой, когда тестировщик сообщает о серьезной проблеме, кто-нибудь цитирует этот раздел, намекая на то, что находить проблемы такого рода не входило в его задачи и… Опа! Продукт и проблема живут дальше бок о бок
  • Конечные пользователи могли бы экономить миллионы долларов, если бы компании, разрабатывающие ПО, перестали писать тест планы
  • Не имеет значения насколько хорошо или плохо написан ваш тест план – тестировщики все равно будут писать тест кейсы так, как они считают нужным
  • На проекте, который длится 4 года, никто и никогда не вспоминает о тест плане
  • Тест план – отличный инструмент для аутсорсинговой компании, который позволяет потребовать денег у заказчика, не проводя при этом тестирование как таковое
  • Каждый, кто принял участие в процессе написания тест плана, испытывает  чувство глубочайшего удовлетворения в тот момент, когда этот документ завершен. При этом не важно, есть ли у них план тестирования или нет
  • Стоимость рецензирования документа, который никто не собирается использовать, слишком высока
  • Тот, кто считает, что он не готов к началу тестирования, поскольку у него нет тест плана, на самом деле не является тестировщиком
  • Небольшой понятный тест план, который вы сможете поддерживать в актуальном состоянии, куда лучше, чем очень большой и подробный до которого никому нет дела
  • Одна хорошая ассоциативная карта  заменит вам тысячу прекрасных тест планов
  • Тест план – это всегда документ, но далеко не всегда это план тестирования
  • Заставляя тестировщика писать документ, который никто не будет читать, не надейтесь, что он вложит в его написание душу
  • Некоторые тест планы устаревают еще до того момента, как в их черновике будет дописана последняя строчка
  • Некоторые рецензенты добиваются того, чтобы тест план был идеален, при этом совсем не факт, что они знают хоть что-нибудь о разрабатываемом продукте
  • Тот, кто знает, что такое альтернативная стоимость, скорее всего, напишет тест план куда лучше того, кто о ней не знает

Thusidides Selenium

Thusidides Selenium

Расскажу про небольшой Framework на Selenium.
Наверное все таки большинство из нас пользуется Selenium WD и знает что наверное самое слабое место у него-это отчеты(вообще говоря их вообще нет),для репортинга в основном используются или плагины или отчеты с Junit или TestNG(если это Java).
Так вот Thsidides призван внести в разработку тестов не только некоторые улучшения,но самое главное хорошие отчеты.

Немного истории!(надо ж как то разнообразить рассказ)
Thusidides переводится как Фукиди́д,был древнегреческим историком,который прославился именно качественными репортами,отсюда и название этого фреймворка.
История закончилась,перейдем к делу.

Тесты набрасывались для примера и чтобы просто показать принцип,естественно они не поддерживаемые и все такое.

Для начала,чтобы подключить его к нашему проекту,добавим в Maven следующее:


  4.0.0

  webtests
  tests
  1.0-SNAPSHOT
  jar

  wikipediawebtests
  http://maven.apache.org

    
        UTF-8
        0.9.88
    

    
        
            junit
            junit
            4.8.2
        
        
            org.hamcrest
            hamcrest-all
            1.1
        
        
            net.thucydides
            thucydides-junit
            ${thucydides.version}
        
        
            org.slf4j
            slf4j-simple
            1.6.1
            pom
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                2.3.2
                
                    1.5
                    1.5
                
            
            
                net.thucydides.maven.plugins
                maven-thucydides-plugin
                ${thucydides.version}
            
        
    
                      

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


public class Application {
    @Feature
        public class TestPageBook {}
        public class TestAuth{}
        public class TestSearch{}
}
Как мы видим,в одной фиче может быть несколько классов и конечно может быть несколько фич.
После этого создается класс с  тестом

@RunWith(ThucydidesRunner.class)
@Story(Application.TestPageBook.class)
public class TestPageBook {

    @Managed
    public WebDriver driver;

    @ManagedPages(defaultUrl = "http://m.megafonpro.ru/")
    public Pages pages;

    @Steps
    public StepsinBook book;

    @Test
    public void testBook() throws Exception {
        book.getMain("http://m.megafonpro.ru/");   //Вход на главную страницу тестируемого ресурса
        book.AllBooks();                                                //Переход во вкладку Все книги      
        book.search();                                                 //Поиск книги с поисковым выражением "Книга
        book.catalog();                                                 //Проверка каталога книг по жанрам

    }
    @Pending @Test              //данная аннотация значит,что тест еще не имплементирован
    public void testBuyBook(){}

}

Далее описываются шаги самого теста,Steps как они называются здесь.

public class StepsinBook extends ScenarioSteps {
    public StepsinBook(Pages pages) {
        super(pages);
    }
    public BookPage getPageBook()
    {
        return  getPages().currentPageAt(BookPage.class);
    }

    @Step
    public void getMain(String url)
    {
        getPageBook().getMainPage(url);
    }

    @Step
    public void AllBooks()
    {
        getPageBook().allBooks();
    }

    @Step
    public void search(){
        getPageBook().search("Книга");
    }

    @Step
    public void catalog(){
        getPageBook().catalog();
    }
}

И привычная нам работа с WD с PageObject.

package Actions;
public class BookPage extends PageObject {
    private ShareFunc func;
    @FindBy(linkText = "Все книги")
    WebElement allbooksButton;

    @FindBy(linkText = "Поиск")
    WebElement searchButton;

    @FindBy(name = "query")
    WebElement searchField;

    @FindBy(css = "button")
    WebElement searchBegin;
    @FindBy(linkText = "Большая книга кремлевских тайн.")
    WebElement searchBook;

    @FindBy(linkText = "Каталог")
    WebElement catalog;

    @FindBy(linkText = "Малая форма")
    WebElement littleForm;

    @FindBy(linkText = "Повести")
    WebElement story;

    @FindBy(linkText = "Всего лишь капелька яда")
    WebElement oneOfStories;

    public BookPage(WebDriver driver) {
        super(driver);
        func=new ShareFunc(driver);

    }

    public void getMainPage(String url) {
        getDriver().get(url);

    }

    public void allBooks() {

        func.safeClick(allbooksButton);

    }

    public void search(String searchWord) {
        func.safeClick(searchButton);
        func.sendKeys(searchField, searchWord);
        func.safeClick(searchBegin);
        func.safeClick(searchBook);

    }

    public void catalog() {
        func.safeClick(catalog,getDriver());
        assertEquals("КАТАЛОГ",findElement(By.cssSelector("div.title > strong")).getText());
        func.safeClick(littleForm);
        assertEquals("МАЛАЯ ФОРМА",findElement(By.cssSelector("div.title > strong")).getText());
        func.safeClick(story, getDriver());
        assertEquals("МАЛАЯ ФОРМА - ПОВЕСТИ",findElement(By.cssSelector("div.title > strong")).getText());
        func.safeClick(oneOfStories);
        assertTrue(getPageSource().contains("Марина Серова"));
    }
}

Иерархия тестов примерно такая,сначала мне не понравилась,но если с ней поработать,то весьма удобно.
Далее в запускаем через maven
mvn test thucydides:aggregate
 
И он нам забацает вот такой отчет.




Немного о более тонкой настройке:
Для выбора запуска браузера запускаем Maven с опцией:
-Dwebdriver.driver=chrome
А для параллельного запсука тестов например используем сигнатуры перед классом с тестами
@RunWith(ThucydidesParameterizedRunner.class)
@Concurrent(threads="3")
 

понедельник, 21 января 2013 г.

Testing SMS and USSD with AT commands

Тестирование через SMS и USSD(с использованием AT комманд)

Бывают такие задачи,когда использование только программной части реализации нам недостаточно,скажем когда при регистрации код подтверждения отправляется нам в смс,автоматизировать такой сценарий-сложно,но можно)
Собственно для автоматизации таких сценариев с использованием мобильных устройств нам понадобится 3g модем,а все остальное я расскажу здесь.
Возможно получится большая статья,но это для того чтобы команда "Р" все сделала правильно!)
Начнем.
Для начала,нам потребуется Java библиотека отсюда-http://smslib.org/
Она будет осуществлять основное взаимодействие между мобильным устройством и нашим тестом.

Поподробнее об установке:
Для корректной работы,этой библиотеки нам понадобятся следующие зависимости:
1.RxTx который можно взять здесь.
После этого 
  • Файл RXTXcomm.jar нужно положить JDKDIR/jre/lib/ext/
  • А нужную библиотеку (для Linux 32bit librxtxSerial.so) в JDKDIR/jre/bin/
2.Smslib использует log4j в качестве логгера,ставим его.
Скачиваем log4j  отсюда. А файл log4j-1.2.15.jar помещаем в ваш classpath или в Java's lib/ext.

3.Apache Jakarta Commons - NET

Скачиваем Apache Jakarta Commons/NET отсюда. А файл commons-net-2.0.jar помещаем в ваш classpath или в Java's lib/ext.


4.JSMPP Library
То же самое с JSMPP ,скачиваем  отсюда и jsmpp-2.1.0.jar помещаем в ваш classpath или в Java's lib/ext.

Тем,кто больше любит Maven-вот пожалуйста
 

 org.rxtx
 rxtx
 2.1.7

            
        
            org.smslib
            smslib
            3.5.2
        
        
            log4j
            log4j
            1.2.17
            
    
    
        
            nightlabs
            nightlabs repo
            http://smslib.googlecode.com/svn/maven2/
        
    

Подключаем модем в USB порт.
Вводим lsub
для вывода подключенных устройств,ищем там свой модем.

Теперь даем права на чтение и запись с портов:
sudo       chmod o+rw /dev/ttyU*

В Java коде пробуем исполнить следующий код(Обращаю внимание,что код не мой,ибо он дико вырвиглазный,а код из доков самой библиотеки):
import org.smslib.helper.CommPortIdentifier;
import org.smslib.helper.SerialPort;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Formatter;

public class CommTest
{
    private static final String _NO_DEVICE_FOUND = "  no device found";

    private final static Formatter _formatter = new Formatter(System.out);

    static CommPortIdentifier portId;

    static Enumeration portList;

    static int bauds[] = { 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 115200 };


    private static Enumeration getCleanPortIdentifiers()
    {
        return CommPortIdentifier.getPortIdentifiers();
    }

    public static void main(String[] args)
    {
        System.out.println("\nSearching for devices...");
        portList = getCleanPortIdentifiers();


        while (portList.hasMoreElements())
        {
            portId = portList.nextElement();
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
            {
                _formatter.format("%nFound port: %-5s%n", portId.getName());
                for (int i = 0; i < bauds.length; i++)
                {
                    SerialPort serialPort = null;
                    _formatter.format("       Trying at %6d...", bauds[i]);
                    try
                    {
                        InputStream inStream;
                        OutputStream outStream;
                        int c;
                        String response;
                        serialPort = portId.open("SMSLibCommTester", 1971);
                        serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN);
                        serialPort.setSerialPortParams(bauds[i], SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                        inStream = serialPort.getInputStream();
                        outStream = serialPort.getOutputStream();
                        serialPort.enableReceiveTimeout(1000);
                        c = inStream.read();
                        while (c != -1)
                            c = inStream.read();
                        outStream.write('A');
                        outStream.write('T');
                        outStream.write('\r');
                        Thread.sleep(1000);
                        response = "";
                        StringBuilder sb = new StringBuilder();
                        c = inStream.read();
                        while (c != -1)
                        {
                            sb.append((char) c);
                            c = inStream.read();
                        }
                        response = sb.toString();
                        if (response.indexOf("OK") >= 0)
                        {
                            try
                            {
                                System.out.print("  Getting Info...");
                                outStream.write('A');
                                outStream.write('T');
                                outStream.write('+');
                                outStream.write('C');
                                outStream.write('G');
                                outStream.write('M');
                                outStream.write('M');
                                outStream.write('\r');
                                response = "";
                                c = inStream.read();
                                while (c != -1)
                                {
                                    response += (char) c;
                                    c = inStream.read();
                                }
                                System.out.println(" Found: " + response.replaceAll("\\s+OK\\s+", "").replaceAll("\n", "").replaceAll("\r", ""));
                            }
                            catch (Exception e)
                            {
                                System.out.println(_NO_DEVICE_FOUND);
                            }
                        }
                        else
                        {
                            System.out.println(_NO_DEVICE_FOUND);
                        }
                    }
                    catch (Exception e)
                    {
                        System.out.print(_NO_DEVICE_FOUND);
                        Throwable cause = e;
                        while (cause.getCause() != null)
                        {
                            cause = cause.getCause();
                        }
                        System.out.println(" (" + cause.getMessage() + ")");
                    }
                    finally
                    {
                        if (serialPort != null)
                        {
                            serialPort.close();
                        }
                    }
                }
            }
        }
        System.out.println("\nTest complete.");
    }
}
Должны в выводе получить,что то вроде:
   Trying at   9600...  Getting Info... Found:
       Trying at  14400...  no device found (
       Trying at  19200...  Getting Info... Found: E352
       Trying at  28800...  no device found
       Trying at  33600...  no device found

Так,если что то нашли,мы на пути к победе.

Дальше все проще будет.

Расскажу на примере получения входящих смс с Java кода.


import org.smslib.AGateway;
import org.smslib.AGateway.Protocols;
import org.smslib.IInboundMessageNotification;
import org.smslib.InboundMessage;
import org.smslib.InboundMessage.MessageClasses;
import org.smslib.Message.MessageTypes;
import org.smslib.Service;
import org.smslib.modem.SerialModemGateway;

import java.util.ArrayList;
import java.util.List;

public class ReadMessages
{
    public void doIt() throws Exception
    {
        List msgList;  //Объявление списка входящих смс
        InboundNotification inboundNotification = new InboundNotification();   //создание метода для оповещений и статуса входящих смс
        try
        {

            // Создаем объект нашего модема,по полученным выше данным
            SerialModemGateway gateway = new SerialModemGateway("modem", "/dev/ttyUSB2", 9600, "Huawei", "E352");
            //Задаем протокол модема,по дефолту PDU,но можно указать и TEXT
            gateway.setProtocol(Protocols.PDU);
            // Мы хотим чтобы модем работал со входящими?
            gateway.setInbound(true);
            //Мы хотим чтобы модем работал с исходящими?
            gateway.setOutbound(true);
            // Указываем SIM PIN.
            gateway.setSimPin("0000");
            // Сеттим наши оповещения
            Service.getInstance().setInboundMessageNotification(inboundNotification);
            // Добавляем сервису наш объект модема
            Service.getInstance().addGateway(gateway);
            // Стартуем сервис
            Service.getInstance().startService();
            // Можно вывести основную инфу(если нужно)
            System.out.println();
            System.out.println("Modem Information:");
            System.out.println("  Manufacturer: " + gateway.getManufacturer());
            System.out.println("  Model: " + gateway.getModel());
            System.out.println("  Serial No: " + gateway.getSerialNo());
            System.out.println("  SIM IMSI: " + gateway.getImsi());
            System.out.println("  Signal Level: " + gateway.getSignalLevel() + " dBm");
            System.out.println("  Battery Level: " + gateway.getBatteryLevel() + "%");
            System.out.println();

            msgList = new ArrayList();
            //Читаем смс
            Service.getInstance().readMessages(msgList, MessageClasses.ALL);
            for (InboundMessage msg : msgList)
                System.out.println(msg);

            System.out.println("Now Sleeping - Hit  to stop service.");
            System.in.read();

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            Service.getInstance().stopService();
        }
    }

    public class InboundNotification implements IInboundMessageNotification
    {
        public void process(AGateway gateway, MessageTypes msgType, InboundMessage msg)
        {
            if (msgType == MessageTypes.INBOUND) System.out.println(">>> New Inbound message detected from Gateway: " + gateway.getGatewayId());
            else if (msgType == MessageTypes.STATUSREPORT) System.out.println(">>> New Inbound Status Report message detected from Gateway: " + gateway.getGatewayId());
            System.out.println(msg);
        }
    }



    public static void main(String args[])
    {
        ReadMessages app = new ReadMessages();
        try
        {
            app.doIt();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
Вроде в комментариях все понятно,но если что спрашиваем в комментах.

И пример с USSD(как же без него)

import org.ajwcc.pduUtils.gsm3040.PduUtils;
import org.smslib.*;
import org.smslib.AGateway.Protocols;
import org.smslib.modem.SerialModemGateway;


public class SendUSSD
{     
    public void doIt() throws Exception
    {

        USSDNotification ussdNotification = new USSDNotification();
        System.out.println("Example: Send USSD Command from a serial gsm modem.");
        System.out.println(Library.getLibraryDescription());
        System.out.println("Version: " + Library.getLibraryVersion());
        SerialModemGateway gateway = new SerialModemGateway("modem.com35", "/dev/ttyUSB2", 9600, "Huawei", "E352");
        gateway.setProtocol(Protocols.PDU);
        gateway.setInbound(true);
        gateway.setOutbound(true);
        gateway.setSimPin("0000");
        Service.getInstance().addGateway(gateway);
        Service.getInstance().setUSSDNotification(ussdNotification);
        Service.getInstance().startService();
        byte[] dataToSend = null;
        byte[] biteToSend = null;
        String stringtoSend = "*100#";
        dataToSend = PduUtils.stringToUnencodedSeptets(stringtoSend);
        biteToSend = PduUtils.encode7bitUserData(null, dataToSend);
        stringtoSend = PduUtils.bytesToPdu(biteToSend);
        System.out.println(stringtoSend);
       //  String resp = gateway.sendUSSDCommand(stringtoSend);  //вообще можно отправлять так,но у меня не заработало,поэтому ниже работает)
       String  resp = gateway.sendCustomATCommand("AT+CUSD=1,\""+stringtoSend+"\",0\r");
        System.out.println(resp);

        System.out.println("Now Sleeping - Hit  to terminate.");
        System.in.read();
        Service.getInstance().stopService();
    }

    public class USSDNotification implements IUSSDNotification
    {
        public void process(AGateway gateway, USSDResponse response) {
            System.out.println("USSD handler called from Gateway: " + gateway.getGatewayId());
            System.out.println(response);
        }
    }


    public static void main(String args[])
    {    
        SendUSSD app = new SendUSSD();
        try
        {
            app.doIt();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Вот и все,не боимся автоматизировать,все что можно.

четверг, 17 января 2013 г.

Подсчет необходимого количества виртуальных пользователей.

Подсчет необходимого количества виртуальных пользователей.


Начнем с примера:
На сайте Google Analytics в средний загруженности день имеем:

  • 2000 посетителей в 60 минут
  • 10,000 просмотров страниц
  • среднее время нахождение на сайте 7 минут

Так,я хочу вывести,сколько виртуальных пользователей мне понадобится для адекватной нагрузки?
Начнем:
2000 пользователей в 1 час (60 минут), 7 минут на сайте.
60 минут / 7минут = 8.5 (пользователей в одном потоке)
2000 / 8.5 = 235 пользователей нужно всего сэмулировать.
На первый взгляд такой расчет кажется весьма логичным и применимым к нашей задаче.Каждый тестовый сценарий должен иметь среднюю продолжительность в 7 минут,чтобы отразить среднее время пребывания на сайте.То есть около 9 пользователей полностью пройдут сценарий в течении часа,т.к. наши пользователи находятся на сайте всего по 7 минут,нам не нужно проверять все 2000 пользователей одновременно.Они долго не задерживаются, поэтому, разделив 2000 пользователей по 8,5 (продолжительность визитов) получим, что около 235 пользователей.
Если представить это в виде формулы:

U = V / (60 / D)
Где:
U-это количество виртуальных пользователей (это то, что мы пытаемся выяснить)
V- это среднее количество посетителей в час
D-является средней продолжительности посетителя
60- это количество минут в часе))
Основной недостаток этого подхода,это то что мы ожидаем равномерного прибытия пользователей,что никак не гарантированно)
Вполне возможно, что из 2000 посетителей в час, 900 из них придут на  сайт в первые 10 минут, затем 400 пользователей в 40 минут, а затем 700 пользователей за последние 10 минут.
С таким раскладом наш тест,даст нам не адекватные данные.
Поэтому для надежности,обязательно смотрим логи сервера,, чтобы получить определение пика пользователей в промежуток времени.
А кому лень считать самому: используем-http://www.webperformance.com/library/tutorials/CalculateNumberOfLoadtestUsers/

четверг, 10 января 2013 г.

Кодировка ответа Sampler в Jmeter.

Кодировка ответа Sampler в Jmeter.

Иногда при работе с Jmeter,в ответах на ваши запросы он использует  неправильную кодировку.
Дело в том,что можно поставить кодировку контента(то бишь отправляемых значений),а получаемых нет.
Это можно исправить(правда способ костыльный,хотя рабочий)-используем BeanShell Jmeter.

Небольшая справка о переменных Jmeter в BeanShell для тех кто не в курсе:
  • log - (Logger) - Можно использовать для записи лога в файл.
  • ctx - (JMeterContext) - Доступ к контексту.
  • vars - ( JMeterVariables ) - Позволяет читать/записывать значения в переменные Jmeter: vars.get(key); vars.put(key,val); vars.putObject("OBJ1",new Object());
  • props - (JMeterProperties - class java.util.Properties) -собственно класс свойств из Java  props.get("START.HMS"); props.put("PROP1","1234");
  • prev - (SampleResult) - Доступ к предыдущему ответу.
  • sampleEvent (SampleEvent) доступ к текущему событию.
Собственно,если мы хотим поменять кодировку ответа на определенный запрос,то к запросу добавляем BS PostProcessor
с кодом

prev.setDataEncoding("UTF-8"); 

И все.Был рад помочь.