вебинар - функциональное тестирование с...

Preview:

Citation preview

Функциональное тестирование с помощью Selenium

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

@andrebrov

Задачи

• Нужно иметь возможность проводить регрессию в короткий период времени

• Тесты должны быть простыми, чтобы их можно было легко написать/дописать/переписать

• Поддержка тестов не должна занимать много времени

Необходимые инструменты

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

Что взяли мы

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

Почему TestNG

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

Почему WebDriver

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

Зачем Spring?

• Облегчение работы с базами данных• Необходима интеграция с различными

сервисами в рамках тестов• IoC

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

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

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() {}}

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

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) {}}

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

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() {}

}

Вынесение данных в 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;  }

}

Refactoring

• Вынесение текстовых констант из классов страниц

• Группировка DataProvider`ов в классы

Подключение базы данных<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.SimpleJdbcTemplate"><constructor-arg ref="dataSource"/></bean>

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

@Componentpublic class SearchByAlternateNameDataProvider {

private static DataProviderGenerator dataProviderGenerator;

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

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

@Componentpublic class DataProviderGenerator {

@Autowiredprivate TestingJdbcTemplate testingJdbcTemplate;

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

}

Хинт 1 – WebDriver как SpringBean

@Configurationpublic class SeleniumConfiguration {@Autowiredprivate WebDriver driver;

public  @Bean  WebDriver driver() {}

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

Хинт 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) {}

}

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

Хинт 4 – Подключаем javascriptpublic 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");}

Подключаем Jenkins

• Используем возможность запуска через maven

• Подключаем отчеты от TestNG и видим результаты регрессии

• Запуск тестов по расписанию / установке новой версии / …

Куда двигаться дальше

• Создание профилей тестирования (smokem full, search)

• Selenium Grid и многопоточность• 1 подход – разные типы приложений

(WebService, ETL, ...)• End-to-end тестирование

• Андрей Ребров• andrebrov@gmail.com

• @andrebrov

Recommended