Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo

1

Бодрящий микс из Selenium и TestNG
Регрессионное тестирование руками
          разработчиков


           Ребров Андрей
               Luxoft

2

@andrebrov

3

Сколько тестировщиков в вашей
           команде?

4

Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разработчиков

5

Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разработчиков

6

Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разработчиков

7

Build more overseer! ©

8

При этом...
• «У нас agile» - значит, тестирование
  должно завершиться в том же спринте
• «Люблю короткие релизы»- значит
  регрессионное тестирование надо делать
  постоянно
• «Они опять изменили требования!» -
  значит опять надо менять тесты

9

Хватит это терпеть!

10

Задачи
• Нужно иметь возможность проводить
  регрессию в короткий период времени
• Тесты должны быть простыми, чтобы их
  можно было легко
  написать/дописать/переписать
• Поддержка тестов не должна занимать
  много времени

11

Необходимые инструменты
• Тестовый фреймворк
• Фреймворк функционального тестирования
• CI Server
+ удобная IDE, понятный генератор отчетов,
удобный язык программирования...

12

Что взяли мы
•   TestNG
•   Selenium 2 / WebDriver
•   Spring
•   IntelliJ IDEA
•   Jenkins
•   Набор самописных утилит

13

Почему TestNG
•   Удобная работа с данными - @DataProvider
•   Разбиение тестов по группам
•   Многопоточность «из коробки»
•   «Фабрика» тестов

14

Почему WebDriver
•   Java-фреймворк
•   Абстракция на уровне PageObject
•   Работа с IE & FF
•   Активно развивается

15

Зачем Spring?
• Облегчение работы с базами данных
• Необходима интеграция с различными
  сервисами в рамках тестов
• IoC

16

Этапы создания тестовой
      платформы

17

Создание базового тестового
          класса

18

public abstract class AbstractSeleniumTestClass extends AbstractTestNGSpringContextTests {

       @Autowired
       private WebDriver driver;

       @BeforeMethod(alwaysRun = true)
       public void printTestName(Method method) {}

       @AfterMethod(alwaysRun = true)
       public void clearCookies(Method method) throws Exception {}

       protected WebDriver getWebDriver() {}

       public SearchPage loadLemAndLogin() {}
   }

19

Создание базовой web-
      страницы

20

public abstract class AbstractPage extends LoadableComponent<LoginPage> {

    public AbstractPage(WebDriver driver) {
      this.driver = driver;
      this.wait = new WebDriverWait(driver, DEFAULT_TIMEOUT);
      PageFactory.initElements(driver, this);
    }

    protected abstract By getPageLoadedCheckElementLocator();

    // Primitive actions
    protected void clickOn(WebElement webElement) {}
    protected void type(WebElement webElement, String text) {}

    // Keys
    protected void pressEnter(WebElement webElement) {}
    protected void pressRight(WebElement webElement) {}
    // Autocomplete
    public void fillAutocomplete(WebElement webElement, String text) {}

    // Waits
    public WebElement waitUntilFound(final By by) {}
}

21

Описание web-страницы

22

public class LoginPage extends AbstractPage {

     private static final Logger log = Logger.getLogger(LoginPage.class);

     @FindBy(xpath = "//input[@name='USER']")
     private WebElement usernameInput;
     @FindBy(xpath = "//input[@name='PASSWORD']")
     private WebElement passwordInput;
     @FindBy(xpath = "//input[@class='Button']")
     private WebElement loginButton;


     @Override
     protected By getPageLoadedCheckElementLocator() {}

     public LoginPage(WebDriver driver) {
       super(driver);
     }

     @Override
     protected void isLoaded() throws Error {}

     public SearchPage login() {}

}

23

Вынесение данных в
                    DataProvider
public class SearchDataProvider {

     @DataProvider
     public static Object[][] searchTypes() {
       Object[][] result = new Object[4][1];
       result[0][0] = "BEGINS_WITH";
       result[1][0] = "CONTAINS";
       result[2][0] = "CONTAINS_SUBSTRING";
       result[3][0] = "SOUNDS_LIKE";
       return result;
     }

}

24

Refactoring
• Вынесение текстовых констант из классов
  страниц
• Группировка DataProvider`ов в классы

25

Подключение базы данных
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-
   method="close">
     <property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
     <property name="url" value=""/>
     <property name="username" value=""/>
     <property name="password" value=""/>
     <property name="maxActive" value="10"/>
</bean>

<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemp
   late">
   <constructor-arg ref="dataSource"/>
</bean>

26

Работа с базой внутри
   DataProvider`ов

27

@Component
public class SearchByAlternateNameDataProvider {
   private static DataProviderGenerator dataProviderGenerator;

    @Autowired
    public void setDataProviderGenerator(DataProviderGenerator dataProviderGenerator) {
    SearchByAlternateNameDataProvider.dataProviderGenerator = dataProviderGenerator;
    }

    @DataProvider
    public static Object[][] alternateNameAndNonSuitableCOI() {
     return dataProviderGenerator.generatePairStringString("select …" + Config.DATA_COUNT);
    }
}

@Component
public class DataProviderGenerator {
   @Autowired
   private TestingJdbcTemplate testingJdbcTemplate;

    public Object[][] generatePairStringString(String sql) {}

}

28

Хинт 1 – WebDriver как
                    SpringBean
@Configuration
public class SeleniumConfiguration {
   @Autowired
   private WebDriver driver;

    public @Bean WebDriver driver() {}

    @PreDestroy
    public void cleanUp() {
      try {
         driver.quit();
      } catch (Throwable e) {
        e.printStackTrace();
      }
    }
}

29

Хинт 2 – TestFactory для похожих
              тестов
public class SearchTestFactory {

     @Factory(dataProvider = "searchTypes", dataProviderClass = SearchDataProvider.class)
     public Object[] createTest(String searchType) {
       return new Object[]{new GenericSearchTest(searchType)};
     }
}

public class GenericSearchTest extends AbstractSeleniumTest {
   private String searchType;

    public GenericSearchByLegalNameCOITest(String searchType) {
      this.searchType = searchType;
    }
    @Test(dataProvider = "legalNamesAndCountries", dataProviderClass =
    SearchTestFactory.class)
    @JiraIssue(number = “SRC-19")
    public void test(String param1, String param2) {}

}

30

Хинт 3 – Unit-тест как тест-кейс
SearchPage searchPage = loadAndLogin();
searchPage.setLegalNameSearchType(searchType);
searchPage.setLegalNameSearchParam(legalName);
SearchResultPage searchResultPage = searchPage.submit();
assertIsSortedByLegalName(searchResultPage);

31

Хинт 4 – Подключаем javascript
public void waitForAjaxComplete() {
   log.verbose("waiting for ajax completion");
      wait.until(new ExpectedCondition<Boolean>() {
         public Boolean apply(WebDriver driver) {
           return (Boolean) js.executeScript("return $.active == 0");
         }
      });
   log.verbose("All ajax calls are complete");
}

32

Подключаем Jenkins
• Используем возможность запуска через
  maven
• Подключаем отчеты от TestNG и видим
  результаты регрессии
• Запуск тестов по расписанию / установке
  новой версии / …

33

Profit!

34

Куда двигаться дальше
• Создание профилей тестирования (smokem
  full, search)
• Selenium Grid и многопоточность
• 1 подход – разные типы приложений
  (WebService, ETL, ...)
• End-to-end тестирование

35

Андрей Ребров
Arebrov@luxoft.com
    @andrebrov

More Related Content

Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разработчиков

  • 1. Бодрящий микс из Selenium и TestNG Регрессионное тестирование руками разработчиков Ребров Андрей Luxoft
  • 3. Сколько тестировщиков в вашей команде?
  • 8. При этом... • «У нас agile» - значит, тестирование должно завершиться в том же спринте • «Люблю короткие релизы»- значит регрессионное тестирование надо делать постоянно • «Они опять изменили требования!» - значит опять надо менять тесты
  • 10. Задачи • Нужно иметь возможность проводить регрессию в короткий период времени • Тесты должны быть простыми, чтобы их можно было легко написать/дописать/переписать • Поддержка тестов не должна занимать много времени
  • 11. Необходимые инструменты • Тестовый фреймворк • Фреймворк функционального тестирования • CI Server + удобная IDE, понятный генератор отчетов, удобный язык программирования...
  • 12. Что взяли мы • TestNG • Selenium 2 / WebDriver • Spring • IntelliJ IDEA • Jenkins • Набор самописных утилит
  • 13. Почему TestNG • Удобная работа с данными - @DataProvider • Разбиение тестов по группам • Многопоточность «из коробки» • «Фабрика» тестов
  • 14. Почему WebDriver • Java-фреймворк • Абстракция на уровне PageObject • Работа с IE & FF • Активно развивается
  • 15. Зачем Spring? • Облегчение работы с базами данных • Необходима интеграция с различными сервисами в рамках тестов • IoC
  • 18. public abstract class AbstractSeleniumTestClass extends AbstractTestNGSpringContextTests { @Autowired private WebDriver driver; @BeforeMethod(alwaysRun = true) public void printTestName(Method method) {} @AfterMethod(alwaysRun = true) public void clearCookies(Method method) throws Exception {} protected WebDriver getWebDriver() {} public SearchPage loadLemAndLogin() {} }
  • 20. public abstract class AbstractPage extends LoadableComponent<LoginPage> { public AbstractPage(WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait(driver, DEFAULT_TIMEOUT); PageFactory.initElements(driver, this); } protected abstract By getPageLoadedCheckElementLocator(); // Primitive actions protected void clickOn(WebElement webElement) {} protected void type(WebElement webElement, String text) {} // Keys protected void pressEnter(WebElement webElement) {} protected void pressRight(WebElement webElement) {} // Autocomplete public void fillAutocomplete(WebElement webElement, String text) {} // Waits public WebElement waitUntilFound(final By by) {} }
  • 22. public class LoginPage extends AbstractPage { private static final Logger log = Logger.getLogger(LoginPage.class); @FindBy(xpath = "//input[@name='USER']") private WebElement usernameInput; @FindBy(xpath = "//input[@name='PASSWORD']") private WebElement passwordInput; @FindBy(xpath = "//input[@class='Button']") private WebElement loginButton; @Override protected By getPageLoadedCheckElementLocator() {} public LoginPage(WebDriver driver) { super(driver); } @Override protected void isLoaded() throws Error {} public SearchPage login() {} }
  • 23. Вынесение данных в DataProvider public class SearchDataProvider { @DataProvider public static Object[][] searchTypes() { Object[][] result = new Object[4][1]; result[0][0] = "BEGINS_WITH"; result[1][0] = "CONTAINS"; result[2][0] = "CONTAINS_SUBSTRING"; result[3][0] = "SOUNDS_LIKE"; return result; } }
  • 24. Refactoring • Вынесение текстовых констант из классов страниц • Группировка DataProvider`ов в классы
  • 25. Подключение базы данных <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy- method="close"> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="url" value=""/> <property name="username" value=""/> <property name="password" value=""/> <property name="maxActive" value="10"/> </bean> <bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemp late"> <constructor-arg ref="dataSource"/> </bean>
  • 26. Работа с базой внутри DataProvider`ов
  • 27. @Component public class SearchByAlternateNameDataProvider { private static DataProviderGenerator dataProviderGenerator; @Autowired public void setDataProviderGenerator(DataProviderGenerator dataProviderGenerator) { SearchByAlternateNameDataProvider.dataProviderGenerator = dataProviderGenerator; } @DataProvider public static Object[][] alternateNameAndNonSuitableCOI() { return dataProviderGenerator.generatePairStringString("select …" + Config.DATA_COUNT); } } @Component public class DataProviderGenerator { @Autowired private TestingJdbcTemplate testingJdbcTemplate; public Object[][] generatePairStringString(String sql) {} }
  • 28. Хинт 1 – WebDriver как SpringBean @Configuration public class SeleniumConfiguration { @Autowired private WebDriver driver; public @Bean WebDriver driver() {} @PreDestroy public void cleanUp() { try { driver.quit(); } catch (Throwable e) { e.printStackTrace(); } } }
  • 29. Хинт 2 – TestFactory для похожих тестов public class SearchTestFactory { @Factory(dataProvider = "searchTypes", dataProviderClass = SearchDataProvider.class) public Object[] createTest(String searchType) { return new Object[]{new GenericSearchTest(searchType)}; } } public class GenericSearchTest extends AbstractSeleniumTest { private String searchType; public GenericSearchByLegalNameCOITest(String searchType) { this.searchType = searchType; } @Test(dataProvider = "legalNamesAndCountries", dataProviderClass = SearchTestFactory.class) @JiraIssue(number = “SRC-19") public void test(String param1, String param2) {} }
  • 30. Хинт 3 – Unit-тест как тест-кейс SearchPage searchPage = loadAndLogin(); searchPage.setLegalNameSearchType(searchType); searchPage.setLegalNameSearchParam(legalName); SearchResultPage searchResultPage = searchPage.submit(); assertIsSortedByLegalName(searchResultPage);
  • 31. Хинт 4 – Подключаем javascript public void waitForAjaxComplete() { log.verbose("waiting for ajax completion"); wait.until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver driver) { return (Boolean) js.executeScript("return $.active == 0"); } }); log.verbose("All ajax calls are complete"); }
  • 32. Подключаем Jenkins • Используем возможность запуска через maven • Подключаем отчеты от TestNG и видим результаты регрессии • Запуск тестов по расписанию / установке новой версии / …
  • 34. Куда двигаться дальше • Создание профилей тестирования (smokem full, search) • Selenium Grid и многопоточность • 1 подход – разные типы приложений (WebService, ETL, ...) • End-to-end тестирование