суббота, 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")