Если вы устали от примитивных "Hello World" примеров и хотите знать больше про кишки Selenide и подводные камни, приходите на этот доклад. Обсудим параллельный запуск, рулы и листенеры, трюки с помощью JavaScript и прокси и всё такое.
А ещё лучше, если вы сами предложите свою тему. Повлияй на доклад!
14. Самый Главный Алгоритм
void click() {
do {
try {
}
catch (Exception e) {
}
} while (прошло меньше N секунд);
}
15. Самый Главный Алгоритм
void click() {
do {
try {
webdriver.findElement().click();
}
catch (Exception e) {
}
} while (прошло меньше N секунд);
}
16. Самый Главный Алгоритм
void click() {
do {
try {
webdriver.findElement().click();
}
catch (Exception e) {
sleep(100 мс)
}
} while (прошло меньше N секунд);
}
17. Дьявол в деталях
void click() {
do {
try {
webdriver.findElement().click();
}
catch (Exception e) {
sleep(100 мс)
}
} while (прошло меньше N секунд);
}
Какие ошибки ловить?
● Некоторые требуют
мгновенного
падения
18. Дьявол в деталях
void click() {
do {
try {
webdriver.findElement().click();
}
catch (Exception e) {
sleep(100 мс)
}
} while (прошло меньше N секунд);
}
А сколько тут ждать?
● Для коллекций
надо ждать
больше?
19. Дьявол в деталях
void click() {
do {
try {
webdriver.findElement().click();
}
catch (Exception e) {
sleep(100 мс)
}
} while (прошло меньше N секунд);
return ???;
}
Результат ок или не ок?
● Например,
$.shouldNot(exist);
20. Дьявол в деталях
void click() {
do {
try {
webdriver.findElement().click();
}
catch (Exception e) {
sleep(100 мс)
}
} while (прошло меньше N секунд);
return …;
}
В какой момент делать
скриншот?
22. Selenide пэдж обжект
public class GooglePage {
public void search(String query) {
$(By.name(“q”))
.val(queue)
.pressEnter();
}
}
● Фактори не нужны.
● Аннотации не нужны.
● Просто программируй!
23. Можно с полем By
public class GooglePage {
private By query = By.name(“q”);
public void search(String query) {
$(query)
.val(queue)
.pressEnter();
}
}
@Test {
var page = new GooglePage();
}
24. Можно с полем SelenideElement
public class GooglePage {
private SelenideElement query = $(By.name(“q”));
public void search(String query) {
query
.val(queue)
.pressEnter();
}
}
@Test {
var page = page(GooglePage.class);
}
25. Селенидовский контейнер
public class ТипаHtmlElementsPage {
SelenideElement container = $(“....”);
SelenideElement username = container.$(“...”);
SelenideElement password = container.$(“...”);
public void login(String un, String pwd) {
username.val(un);
password.val(pwd);
}
И контейнеры не нужны!
26. Есть и контейнеры
class ТипаHtmlElementsPage {
@FindBy(id = "status")
StatusBlock status;
}
class StatusBlock extends ElementsContainer {
@FindBy(className = "username")
SelenideElement username;
@FindBy(className = "username")
SelenideElement username;
} Повторюсь:
Я не уверен, что это нужно.
32. Есть два способа:
setWebDriver WebdriverProvider
Не рекомендую! Рекомендую
● Сам думай, когда
закрыть
● Заботишься
только о том, КАК
открыть браузер
33. setWebDriver
@Before
public void setUp() {
this.browser = new ChromeDriver(........);
WebDriverRunner.setWebDriver(browser);
}
@After
public void tearDown() {
WebDriverRunner.closeWebDriver();
browser.quit();
34. WebdriverProvider
@Before
public void setUp() {
Configuration.browser = MyWDProvider.class.getName();
open(“https://google.com”);
}
@After
public void tearDown() {
// ничего не нужно
}
35. WebdriverProvider
static class MyWDProvider implements WebDriverProvider {
@Override
public WebDriver createDriver(DesiredCapabilities сapabilities) {
return new ChromeDriver(desiredCapabilities);
}
}
36. WebdriverProvider
static class MyWDProvider implements WebDriverProvider {
@Override
public WebDriver createDriver(DesiredCapabilities сapabilities) {
ChromeOptions options = new ChromeOptions();
options.setHeadless(true);
options.addArguments("--proxy-bypass-list=<-loopback>");
options.merge(desiredCapabilities);
return new ChromeDriver(options);
}
}
40. А вот это низя:
Поток 1:
Configuration.timeout = 200;
open(“/user”);
$(“a”).should(appear);
Поток 2:
Configuration.timeout = 9999999;
open(“/admin”);
$(“#active-users”).should(...);
41. @Test
public void userLogin() {
open(“http://some.site.com”);
$(“#username”).val(“petja”).pressEnter();
$(“#name”).shouldHave(text(“Hello, Petja!”);
}
И так низя:
42. @Test
public void userLogin() {
open(“http://some.site.com”);
$(“#username”).val(“petja”).pressEnter();
$(“#name”).shouldHave(text(“Hello, Petja!”);
open(“http://some.site.com/admin”);
$(“.block[data-username=’petja’]”).click();
refresh();
$(“#name”).shouldHave(text(“Goodbye, Petja!”);
}
И так низя:
43. @Test
public void userLogin() {
var b1 = new SelenideDriver();
b1.open(“http://some.site.com”);
b1.$(“#username”).val(“petja”).pressEnter();
b1.$(“#name”).shouldHave(text(“Hello, Petja!”);
var b2 = new SelenideDriver();
b2.open(“http://some.site.com/admin”);
b2.$(“.block[data-username=’petja’]”).click();
b1.$(“#name”).shouldHave(text(“Goodbye, Petja!”);
Selenide 5.0
44. var b1 = new SelenideDriver(
new SelenideConfig()
.timeout(6000)
.browser("chrome")
.headless(true)
);
var b2 = new SelenideDriver(
new SelenideConfig()
.timeout(90_000)
.browser("firefox")
.proxyEnabled(true)
);
Selenide 5.0
51. Для $.download...
Selenide запускает свой прокси-сервер.
Он умеет:
● Скачивать файлы
● Подменять заголовки
● Инжектить JS код на страницу
● И т.д. и т.п.
56. Люди запускают тесты в N потоков
Проблемы:
● Статические переменные
● Одновременный доступ к данным
● синхронизация
● И т.д.
● - ОШИБКИ!
Параллелизация
57. Запускать тесты в N процессов
pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>3</forkCount>
<reuseForks>true</reuseForks>
</configuration>
</plugin>
Гораздо проще -
58. Запускать тесты в N процессов
buld.gradle:
test {
maxParallelForks = 5
}
Гораздо проще -
65. Кол-во видимых элементов
@Test {
$$(".offer:visible").shouldHave(size(3));
}
@Test {
$$(".offer").filter(visible)
.shouldHave(size(3));
}
Selenium не умеет
Это м.б. медленно :(
66. Кол-во видимых элементов
int sizeOf(String cssSelector) {
Number count = executeJavaScript(
"return $(arguments[0]).length", cssSelector);
return count.intValue();
}
@Test {
assertEquals(1, sizeOf(".offer:visible"));
}
67. Из списка элементов исключить
другой список
List<String> страныИзЭксельки = ...;
List<String> страныСоСтраницы = $$(“td:nth-child(3)”)
.excludeWith(text(“text”))
.stream()
.map(el -> el.getText())
.collect(toList());
Это м.б. медленно :(
68. Из списка элементов исключить
другой список
List<String> страныИзЭксельки = ...;
List<String> страныСоСтраницы = executeJavascript(
"return Array.from(" +
" document.querySelectorAll('td:nth-child(3)')" +
").map(x => x.textContent)");
А вот это быстро
70. Проблема:
1. $.sendKeys() медленный
2. Иногда $.sendKeys() не работает:
a. Элемент невидимый
b. Маска на поле
c. Какой-нибудь хитрый JS
d. Какой-нибудь компонент типа DatePicker
На помощь приходит JavaScript!
71. Режим “fast set value”
mvn -Dselenide.fastSetValue=true
@Before public void setUp() {
Configuration.fastSetValue = true;
}
или
72. Режим “fast set value”
И тогда $.setValue() использует JavaScript
который гораздо быстрее!
73. Ты сам можешь выбирать:
$.setValue(“john”) - быстро
$.sendKeys(“john”) - если быстро никак
● Автозаполнение
● Автоформатирование
● Хитрые JS обработчики
● JS почему-то не работает
● ...
80. Selenide для мобильников (Appium)
@Test public void mobileCalculator() {
$(By.name("2")).click();
$(By.name("+")).click();
$(By.name("4")).click();
$(By.name("=")).click();
$(By.className("android.widget.EditText"))
.shouldHave(text("6"));
}
Можно без аннотаций:
https://github.com/selenide-examples
/selenide-appium
81. Пэдж обжекты и Appium
class MobileCalculatorPage {
@AndroidFindBy(id = "op_add")
@iOSFindBy(id = “op_add”);
SelenideElement plus;
}
А можно и с аннотациями:
https://github.com/selenide
/selenide-appium