Архитектура. Основные элементы.
Что же такое архитектура? Это, по сути своей, структура разрабатываемой программы, описание того, как внутри устроены компоненты и как они друг с другом взаимодействуют. Кто-то может подумать , мол это же просто автотесты, зачем тут такие сложные процессы, проектирование архитектуры. Но это очень большое заблуждение.
Отсутствие архитектуры при проектировании тестового фреймворка приводит к многочисленным переписываниям кода,что в свою очередь требует серьезных временных затрат. Но, как говорится, "Время - деньги". Следствие всего этого - потеря прибыли и недовольство заказчиков. Перейдём непосредственно к важным частям архитектуры.
Центральное место в архитектуре для автоматизации занимает паттерн Page Object, с которым вы уже знакомы. Он позволяет отделить логику описания веб-страниц от логики тестов. Также не будем забывать и о Page Element, который позволяет инкапсулировать работы с кастомными элементами.
При работе с паттерном Page Object, у нас возникает проблема: где удобнее хранить локаторы? Решить её можно создав UI Map. Для этого можно воспользоваться вариантами, описанными ниже. Один из них это использовать файлы .properties.
1
.properties — текстовый формат и одноименное расширение имени файла, применяемое для сохранения конфигурационных параметров
Copied!
Каждый параметр сохраняется как пара строк, первая содержит имя параметра (ключ), вторая значение параметра. Ключ и его значение в файле разделяются знаком равенства (ключ=значение). К примеру, вы создали файл locators.properties, а его содержимое будет выглядеть так:
1
LoginPage.title=Login Page
2
LoginPage.userNameInput=id=Login
3
LoginPage.userPassInput=id=Password
4
LoginPage.loginButton=css=input[value=Login]
5
Grid.FieldFrom=xpath=.//input[contains(@placeholder,'From')]
6
Grid.Datepicker=css=.datepicker
7
HomePage.title=Games
Copied!
А чтобы использовать содержимое Properties файла, мы напишем парсер, который нам в этом поможет:
1
public class Locators {
2
private static final Properties locators;
3
4
private enum LocatorType{
5
id, name, css, xpath, tag, text, partText;
6
}
7
8
static {
9
locators = new Properties();
10
InputStream is = Locators.class.getResourceAsStream("/locators.properties");
11
try {
12
locators.load(is);
13
}
14
catch (Exception e) {
15
System.out.println(e.getMessage());
16
}
17
}
18
19
public static String title(String pageName) {
20
return locators.getProperty(pageName);
21
}
22
23
public static By get(String locatorName) {
24
String propertyValue = locators.getProperty(locatorName);
25
return getLocator(propertyValue);
26
}
27
28
public static By get(String locatorName, String parameter) {
29
String propertyValue = locators.getProperty(locatorName);
30
return getLocator(String.format(propertyValue, parameter));
31
}
32
33
private static By getLocator(String locator){
34
String[] locatorItems = locator.split("=",2);
35
LocatorType locatorType = LocatorType.valueOf(locatorItems[0]);
36
37
switch(locatorType) {
38
39
case id :{
40
return By.id(locatorItems[1]);
41
}
42
43
case name:{
44
return By.name(locatorItems[1]);
45
}
46
47
case css:{
48
return By.cssSelector(locatorItems[1]);
49
}
50
51
case xpath:{
52
return By.xpath(locatorItems[1]);
53
}
54
55
case tag:{
56
return By.tagName(locatorItems[1]);
57
}
58
59
case text:{
60
return By.linkText(locatorItems[1]);
61
}
62
63
case partText:{
64
return By.partialLinkText(locatorItems[1]);
65
}
66
67
default:{
68
throw new IllegalArgumentException("No such locator type: " + locatorItems[0]);
69
}
70
}
71
}
Copied!
Другой способ организации работы с локаторами - это использование Html Elements, который объединяет элементы в блоки. Вот как выглядит пример такого блока:
1
@Name("Search form")
2
@Block(@FindBy(xpath = "//form"))
3
public class SearchArrow extends HtmlElement {
4
@Name("Search request input")
5
@FindBy(id = "searchInput")
6
private TextInput requestInput;
7
8
@Name("Search button")
9
@FindBy(className = "b-form-button__input")
10
private Button searchButton;
11
12
public void search(String request) {
13
requestInput.sendKeys(request);
14
searchButton.click();
15
}
16
}
Copied!
Тогда Page Object будет описывать следующим образом:
1
public class SearchPage {
2
private SearchArrow searchArrow;
3
// Other blocks and elements here
4
5
public SearchPage(WebDriver driver) {
6
PageFactory.initElements(new HtmlElementDecorator(driver), this);
7
}
8
9
public void search(String request) {
10
searchArrow.search(request);
11
}
12
13
// Other methods here
14
}
Copied!
Еще одной важной частью архитектуры является создание Helper классов (вспомогательных классов). В них мы можем пометить методы, которые мы применяем на многих страницах, чтобы избежать дублирования кода, следуя принципу Don't repeat youself. В такие классы можно поместить реализацию кастомных ожиданий или реализацию сложных действий:
1
public class Waiter {
2
private static final int DEFAULT_TIME_OUT = 10;
3
4
public static void waitFor(final ExpectedCondition condition){
5
getWaiter().until(condition);
6
}
7
8
public static void waitForJquery(){
9
getWaiter().until(new ExpectedCondition<Boolean>() {
10
@Override
11
public Boolean apply(WebDriver webDriver) {
12
JavascriptExecutor js = (JavascriptExecutor) webDriver;
13
return (Boolean) js.executeScript("return jQuery.active == 0");
14
}
15
});
16
}
17
18
//other waiters
19
}
Copied!
Также полезным окажется применение шаблона Flow. Он заключается в том, что описывая функционал веб-страниц, в методах мы возвращаем либо саму страницу, либо объект другой страницы. Такой подход еще больше похож на то, как работает пользователь: он либо выполняет действия на странице и никуда не переходит, либо нажав на какую-либо кнопку оказывается на другой странице веб-приложения.
Вот, пожалуй, одни из основных архитектурных решений, которые упростят вам жизнь как автоматизаторам. Однако, хочется сразу сказать, что универсального решения нет. Все зависит от особенностей проекта, используемых технологий и желания заказчика.
Copy link