Бодрящий микс из Selenium и TestNG

Page 1

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


@andrebrov


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





Build more overseer! Š


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


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


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


Необходимые инструменты • Тестовый фреймворк • Фреймворк функционального тестирования • 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" destroymethod="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>


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


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

}


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


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


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


Profit!


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


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


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.