Тройничок
Selenide�для Web, Android и iOS
Andrei Solntsev
2. для web
3. для Android
4. для iOS
5. И всё вместе!
План
Selenide
бесплатный
опен-сорс
лаконичный API
стабильные тесты
@jselenide
selenide.org
UI Test
@Test
public void userCanLogin() {
open("http://localhost:8080/login");
$(By.name("user.name")).setValue("john");
$("#submit").click();
$(".menu").shouldHave(text("Hello, John!"));
}
UI Test
@Test
public void userCanLogin() {
open("http://localhost:8080/login");
$(By.name("user.name")).setValue("john");
$("#submit").click();
$(".menu").shouldHave(text("Hello, John!"));
}
UI Test
@Test
public void userCanLogin() {
open("http://localhost:8080/login");
$(By.name("user.name")).setValue("john");
$("#submit").click();
$(".menu").shouldHave(text("Hello, John!"));
}
UI Test
@Test
public void userCanLogin() {
open("http://localhost:8080/login");
$(By.name("user.name")).setValue("john");
$("#submit").click();
$(".menu").shouldHave(text("Hello, John!"));
}
UI Test
@Test
public void userCanLogin() {
open("http://localhost:8080/login");
$(By.name("user.name")).setValue("john");
$("#submit").click();
$(".menu").shouldHave(text("Hello, John!"));
}
Selenide
против ОЖИДАНИЙ
Решения для проблем с Ajax:
бедно
многословно
идеально!
1. Implicit wait
3. $("h1").shouldBe(visible);
2. new WebDriverWait(driver, 1000).until(� visibilityOfElementLocatedBy(By.tagName(“h1”)));
Selenide
фишечки
Умные ожидания
Забудь про ожидания!
$(".loading_progress").shouldBe(visible);�
$("#menu").shouldHave(text("Привіт, Петро!"));�
$(By.name("gender")).shouldNotBe(selected);�
$(By.name("gender")).should(disappear);
Умные ожидания
$(".loading_progress").shouldBe(visible);
$.should*() методы �ждут�до 4 секунд
Умные ожидания
$(".loading_progress").shouldBe(visible);
Таймаут настраивается
mvn -Dselenide.timeout=8000
Сообщения об ошибках
$(“.alert”).shouldNotHave(
text(“Помилка”));
Element should not have text Помилка' {.alert}�Element: '<div class=info alert>Помилка</div>'�Timeout: 4 s.
screenshot + html
Element should be hidden {#gameWin}
Element: '<img class="gameOver" id="gameWin" src="img/thumbs-up.jpeg"></img>'
Screenshot: file:/.../hangman/build/reports/tests/1510751914648.0.png
Page source: file:/.../hangman/build/reports/tests/1510751914648.0.html
Timeout: 4 s.
Debug with pleasure!
Коллекции
$$(“.error”).shouldHave(size(3));
$$ возвращает список элементов:
$ возвращает первый элемент
Коллекции
$$(“#employees tbody tr”).shouldHave(
texts(
“John Belushi”,
“Bruce Willis”,
“John Malkovich”
)�);
Коллекции: фильтрация
$$(“#employees tbody tr”)
.filter(visible)
.shouldHave(size(4));
Навигация
Поиск элементов
$("#loginBtn")
= $(By.id("loginBtn"))
= $(By.className("active"))
= $(By.xpath("//div//h1"))
= $("[name=username]")
$x("//div//h1")
$(By.name("username"))
$(".active")
Поиск по атрибутам
import static com.codeborne.selenide.Selectors.*;�
$(by("type", "checkbox"))
$(by("readonly", "readonly"))
$(byTitle("Login form"))
$(byValue("Alert button"))
$(by(“data-test-id”, "alert-button"))
�
Поиск по атрибутам
$(by(“data-test-id”, "alert-button"))
�
<button id=”some-id-for-devs”
data-design-id=”some_id_for_css”
data-test-id=”someIdForTests”>
Alert button
</button>
Поиск по тексту
import static com.codeborne.selenide.Selectors.*;�
$(byText("Hello, Київ!"))� .shouldBe(visible);�
$(withText("ello"))� .shouldHave(text("Hello, Київ!"));
Найди родителей
Найди предков и потомков
$(“table td[data-foo=’bar’]”)
.closest(“table”)
.find(“tr”, 4)
.find(byText(“THE CELL”)
.click();
Кастомные проверки
$(“h1”).shouldHave(
);
css(“font-size”, “16px”)
public static Condition css(String name, String value) {
return new Condition(“css”) {
public boolean apply(Driver driver, WebElement el) {
return value.equals(el.getCssValue(name));
}
}
}
Кастомные команды
// A helper method
void doubleClick(WebElement element, long delay) {
element.click();
sleep(delay);
element.click();
}
@Test {
doubleClick($(“#slider”), 100);
}
Но было бы клёвее так:
@Test {
$(“#slider”).doubleClick(100);
}
@Test {
doubleClick($(“#slider”), 100);
}
Или ещё клёвее:
@Test {
$(“#slider”)
.doubleClick(100)
.tripleClick(15)
.quadroClick(50)
}
Так можно в Kotlin и Groovy
Но не в Java
Но в Java можно почти так же:
@Test {
$(“#slider”)
.execute(doubleClick(100))
.execute(tripleClick(15))
.execute(quadroClick(50))
}
Command<SelenideElement> doubleClick(long delay) {
return (proxy, locator, args) -> {
proxy.click(); … proxy.click();
return proxy;
};
}
См. https://ru.selenide.org/2019/09/02/selenide-5.3.0/
Браузеры
Selenide и браузеры
java -Dselenide.browser=chrome
java -Dselenide.browser=firefox
java -Dselenide.browser=ie
java -Dselenide.browser=cy.test.MyWebdriver
...
Вариант 1: system property
build.gradle:
task chrome(type: Test) {
systemProperties['selenide.browser'] = 'chrome'
}
task firefox(type: Test) {
systemProperties['selenide.browser'] = 'firefox'
}
task ie(type: Test) {
systemProperties['selenide.browser'] = 'ie'
}
Jenkins
Job 1: ./gradlew chrome
Job 2: ./gradlew firefox
Job 3: ./gradlew ie
Selenide и браузеры
@Before
public void setUp() {
Configuration.browser = “chrome”;
// or Configuration.browser = “ie”;
// or Configuration.browser = “cy.MyWebdriver”;
open(“https://google.com”);
}
Вариант 2: прямо в коде
Что если нужен
кастомный вебдрайвер?
WebdriverProvider
@Before
public void setUp() {
Configuration.browser = MyWDProvider.class.getName();
open(“https://google.com”);
}
WebdriverProvider
static class MyWDProvider implements WebDriverProvider {
@Override
public WebDriver createDriver(DesiredCapabilities сapabilities) {
return new ChromeDriver(capabilities);
}
}
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);
}
}
Об’єкт сторінки
Selenide пэдж обжект
public class GooglePage {
public void search(String query) {
$(By.name(“q”))
.val(queue)
.pressEnter();
}
}
Внести поле “By”
public class GooglePage {
private By query = By.name(“q”);
public void search(String query) {
$(query)
.val(queue)
.pressEnter();
}
}
@Test {
var page = new GooglePage();
}
Вынести поле “SelenideElement”
public class GooglePage {
private SelenideElement query = $(By.name(“q”));
public void search(String query) {
query
.val(queue)
.pressEnter();
}
}
Вынести поле @FindBy
public class GooglePage {
@FindBy(name = “q”)
private SelenideElement query;
public void search(String query) {
query
.val(queue)
.pressEnter();
}
}
@Test {
var page = page(GooglePage.class);
}
Selenide
для
мобильников
Эмуляция мобильного браузера
task chrome(type: Test) {
systemProperties['selenide.browser'] = 'chrome'
}
task chrome-mobile(type: Test) {
systemProperties['selenide.browser'] = 'chrome'
systemProperties['chromeoptions.mobileEmulation'] = 'Nexus 5'
}
Тесты для мобильников
Удалённый
Нативный
Тест на Kotlin/Swift
Тест на любом ЯП
Тесты для мобильников
Удалённый
Нативный
Тест на Kotlin/Swift
Тест на любом ЯП
Мы сейчас здесь
Selenide для мобильников (Appium)
https://github.com/selenide-examples� /selenide-appium
Без аннотаций
С аннотациями
https://github.com/selenide� /selenide-appium
Selenide для мобильников
Простой вариант:
без аннотаций
@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
Test setup
public class CalculatorTest {
@Before
public void setUp() {
Configuration.startMaximized = false;
Configuration.browserSize = null;
Configuration.browser = MyAndroidDriver.class.getName();
open();
}
}
Webdriver setup: Android
public class MyAndroidDriver implements WebDriverProvider {
@Override
public WebDriver createDriver(DesiredCapabilities caps) {
caps.setCapability(“version”, "4.4.2");
caps.setCapability("automationName", "Appium");
caps.setCapability("platformName", "Android");
caps.setCapability("deviceName", "0123456789ABCDEF");
caps.setCapability(“appPackage”, "com.android.calculator2");
caps.setCapability(“appActivity”, "com.android.calculator2.Calculator");
return new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), caps);
}
}
Webdriver setup: iOS
public class MyIOSDriver implements WebDriverProvider {
@Override
public WebDriver createDriver(DesiredCapabilities caps) {
caps.setCapability("platformName", "iOS");
caps.setCapability("deviceName", "iPhone 11");
caps.setCapability("platformVersion", "13.0");
caps.setCapability("app", resourcePath("apks/Wikipedia.app"));
caps.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, "16");
capabilities.setCapability("autoAcceptAlerts", true);
return new IOSDriver("http://127.0.0.1:4723/wd/hub", caps);
}
}
Было бы круто �переиспользовать
тестовый код
для Web, Android, iOS!
Selenide для мобильников
Продвинутый:
с аннотациями
Пэдж обжект и Appium
class MobileCalculatorPage {
@FindBy(id = "op_add")
@AndroidFindBy(id = "op-add")
@iOSFindBy(id = “operandAdd”);
private SelenideElement plus;
}
С аннотациями:
https://github.com/selenide� /selenide-appium
Пэдж обжект и Appium
class CalculatorPage {
@AndroidFindBy(id = "op_add")
@iOSFindBy(id = "op_add")
SelenideElement plus;
@AndroidFindBy(id = "eq")
SelenideElement equal;
@AndroidFindBy(id = "digit_2")
WebElement number2;
@AndroidFindBy(id = "result")
SelenideElement result;
}
Тест с пэдж обжектом
@Test
public void pageObject() {
CalculatorPage page = screen(CalculatorPage.class);
page.number2.click();
page.plus.click();
page.number4.click();
page.equal.click();
page.result.shouldHave(text("6"));
}
Wikipedia-Selenide
Плюсы
Плюсы
Проблемы
Appium - это JavaScript
(лично меня) БЕСИТ
Appium сырой
Appium для Selenium
-
как геометрия Лобачевского для геометрии евклидовой:
всё круглое и через жопу.
Стандартные методы не работают
Вызываю window().setSize(...)
org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: Could not proxy. Proxy error: Could not proxy command to remote server. Original error: 404 - undefined
Смотрим в логи аппиума:
[W3C (04cceb89)] Driver proxy active, passing request on via HTTP proxy
[debug] [WD Proxy] Matched '/wd/hub/session/04cceb89-3c9b-46d9-b5c4-eea0a61693c4/window/rect' to command name 'setWindowRect'
[debug] [WD Proxy] Proxying [POST /wd/hub/session/04cceb89-3c9b-46d9-b5c4-eea0a61693c4/window/rect] to [POST http://localhost:8201/wd/hub/session/b4cc73f9-bf84-45fa-b2dd-7301e8255d14/window/rect] with body: {"width":1366,"height":768}
[WD Proxy] 404 - undefined
[debug] [WD Proxy] StatusCodeError: 404 - undefined
[debug] [WD Proxy] at new StatusCodeError (/usr/local/lib/node_modules/appium/node_modules/request-promise-core/lib/errors.js:32:15)
[debug] [W3C (04cceb89)] Encountered internal error running command: Error: Could not proxy. Proxy error: Could not proxy command to remote server. Original error: 404 - undefined
Что дальше?
Тесты для мобильников
Удалённый
Нативный
Selenide для Espresso?
Andrei Solntsev
selenide.org
Дуже дякую!
More videos
asolntsev.github.io/ru/video/
twitter.com/asolntsev
Статистика скачиваний
SeleniumCamp 2015
SeleniumCamp 2020
Thanks for the photos: