Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]

Post on 16-Apr-2017

1.574 views 9 download

Transcript of Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]

Selenide Alternative in PythonIntroducing Selene

Preface…

Alternative in PythonSelenideIntroducing Selene

Selenide

Selenide =

Selenide = ?

Selenide = … web automation tool

Selenide = … web automation tool

selenium wrapper

Selenide = … web automation tool

selenium wrapper

Selenide = Effectiveweb test automation tool

Selenide = Effectiveweb test automation tool

being also selenium wrapper

Selenide = Effectiveweb test automation tool

=

?

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser(it should be already

automated;)

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

dynamic elements

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

dynamic elements

? = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

dynamic elements

Selene = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

dynamic elements

“UI Tests Logic” Automation with Selene

class TestTodoMVC(BaseTest): def test_filter_active_tasks(self): # visit page # add "a" # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

# visit page # add "a" # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

visit("https://todomvc4tasj.herokuapp.com/") # add "a" # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Concise API

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Concise API

search element “short-cut”

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Concise API

default conversion to “by css” locator

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Concise API

with implicit clear()

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Concise API

chainable methods

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Dynamic Elements

search actually starts here

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() # add "b" # add "c" # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Waiting Search

with implicit waiting for visibility

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() # tasks should be "a", "b", "c" # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(exact_texts("a", "b", "c")) # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Waiting Asserts

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(exact_texts("a", "b", "c")) # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

Waiting Asserts

aka “explicit waits”

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) # toggle "b" # filter active # tasks should be "a", "c"

UI Tests Logic

handy conditions

Waiting Asserts

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).find(".toggle").click() # filter active # tasks should be "a", "c"

UI Tests Logic

Concise API & Waiting Search

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).find(".toggle").click() # filter active # tasks should be "a", "c"

UI Tests Logic

laconic inner collection search by text

Concise API & Waiting Search

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).find(".toggle").click() # filter active # tasks should be "a", "c"

UI Tests Logic

laconic inner collection search by text

instead of bulky xpath locators

Concise API & Waiting Search

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).find(".toggle").click() # filter active # tasks should be "a", "c"

UI Tests Logic

inner element search

Concise API & Waiting Search

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).s(".toggle").click() # filter active # tasks should be "a", "c"

UI Tests Logic

handy alias

Concise API & Waiting Search

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).s(".toggle").click() s(by_link_text("Active")).click() # tasks should be "a", "c"

UI Tests Logic

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text("b")).s(".toggle").click() s(by_link_text("Active")).click() # tasks should be "a", "c"

UI Tests Logic

custom locators

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text(“b")).s(".toggle").click() s(by_link_text("Active")).click() tasks.filterBy(visible).should_have(texts("a", "c"))

UI Tests Logic

Concise API & Waiting Asserts

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text(“b")).s(".toggle").click() s(by_link_text("Active")).click() tasks.filterBy(visible).should_have(texts("a", "c"))

UI Tests Logic

Concise API & Waiting Asserts

filtering collection

visit("https://todomvc4tasj.herokuapp.com/") s("#new-todo").set("a").press_enter() s("#new-todo").set("b").press_enter() s("#new-todo").set("c").press_enter() ss("#todo-list li").should_have(texts("a", "b", "c")) ss("#todo-list li").findBy(text(“b")).s(".toggle").click() s(by_link_text("Active")).click() tasks.filterBy(visible).should_have(texts("a", "c"))

UI Tests Logic

Page steps for even more readable code?

#tasks.py

def visit(): tools.visit("https://todomvc4tasj.herokuapp.com/")

def add(*task_texts): for text in task_texts: s("#new-todo").set(text).press_enter() def filter_active(): s(by_link_text(”Active”)).click() def filter_completed(): s(by_link_text(”Completed”)).click()

#tasks.py

tasks = ss("#todo-list>li") ...

def toggle(task_text): tasks.findBy(text(task_text)).find(".toggle").click() def should_be(*task_texts): tasks.filterBy(visible).should_have(texts(*task_texts))

tasks = ss("#todo-list>li")...

tasks = ss("#todo-list>li")...

Dynamic Elements

tasks = ss("#todo-list>li")...

Dynamic Elements

possible because search does not start here

tasks = ss("#todo-list>li")...

Dynamic Elements

possible because search does not start here

in fact, ss creates “lazy elements proxy” ;)

class TestTodoMVC(BaseTest): def test_filter_tasks(self): tasks.visit() tasks.add("a", "b", "c") tasks.should_be("a", "b", "c") tasks.toggle("b") tasks.filter_active () tasks.should_be("a", "c") tasks.filter_completed () tasks.should_be("b")

Unfortunately still some “automating browser” low level

code needed for setup

class TestTodoMVC(BaseTest): def test_filter_tasks(self): tasks.visit() tasks.add("a", "b", "c") tasks.should_be("a", "b", "c") tasks.toggle("b") tasks.filter_active () tasks.should_be("a", "c") tasks.filter_completed () tasks.should_be("b")

@pytest.fixture(scope='class')def setup(request): set_driver(webdriver.Firefox()) def teardown(): get_driver().quit() request.addfinalizer(teardown)@pytest.mark.usefixtures("setup")class BaseTest(object): pass

Customisation

config.app_host = "http://mydomain.com" ... visit("/subpage")

s("#new-todo").should_be(enabled)

Default Timeouts Behaviour

will wait until 4 seconds

s("#new-todo").should_be(enabled, timeout=10)

Custom

or

config.timeout = 10 ... s("#new-todo").should_be(enabled)

Widgets for even more structural OOP code?

Selene as htmlelements alternative ;)

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Autocompletion as a bonus

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

Inheriting all Selene element’s behaviour

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

search inside self

Standalone custom element aka Widget

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click()def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete() ss("#todo-list>li").assure(texts("b"))

search inside self

def test_custom_selements(): given_active("a", "b") page = TodoMVC() page.tasks.find(text("b")).toggle() page.clear_completed() page.tasks.assure(texts("a"))

PageObjects of “plural” Widgets: Usage

def test_nested_custom_selements(): given_active("a", "b") page = TodoMVC() page.tasks.find(text("b")).toggle() page.clear_completed() page.tasks.assure(texts("a"))

PageObjects of “plural” Widgets: Usage

Autocompletion?

Autocompletion?

NO :’(

PageObjects of “plural” Widgets: Implementation

class TodoMVC(object): def __init__(self): self.tasks = ss("#todo-list>li").of(self.Task) def clear_completed(self): s(“#clear-completed").click() class Task(SElement): def toggle(self): self.s(".toggle").click() return self

PageObjects of “plural” Widgets: Implementation

class TodoMVC(object): def __init__(self): self.tasks = ss("#todo-list>li").of(self.Task) def clear_completed(self): s(“#clear-completed").click() class Task(SElement): def toggle(self): self.s(".toggle").click() return self

Just keep balance…

class SelectList(SElement): def __init__(self, locator, context=RootSElement()): super(SelectList, self).__init__(locator, context) def set(self, value): self.assure(visible) Select(self.found).select_by_visible_text(value) return self

... s('[name="first_name"]').set('Iakiv')s('[name="last_name"]').set('Kramarenko') SelectList('#salutation').set('mr')

Good case for reusable Widget

class SelectList(SElement): def __init__(self, locator, context=RootSElement()): super(SelectList, self).__init__(locator, context) def set(self, value): self.assure(visible) Select(self.found).select_by_visible_text(value) return self

... s('[name="first_name"]').set('Iakiv') s('[name="last_name"]').set('Kramarenko') SelectList('#salutation').set('mr')

Good case for reusable Widget

Doubtful…class TodoMVC(object): def __init__(self): self.tasks = ss("#todo-list>li").of(self.Task) class Task(SElement): def toggle(self): self.s(".toggle").click() return self

vs

tasks = ss("#todo-list>li")

def toggle(task_text): tasks.findBy(text(task_text)).find(".toggle").click()

Doubtful…

vs

tasks.toggle("b")

main.tasks.find(text("b")).toggle()

Keep It Simple Stupid!

KISS Automation

Plain Pages over Pages of Nested Widgets

KISS Automation

Plain Pages over Pages of Nested Widgets

Page Modules over PageObjects

KISS Automation

Plain Pages over Pages of Nested Widgets

Page Modules over PageObjects

Automating UI tests logic over low level coding

Q&A

github.com/yashaka

github.com/yashaka/selene

yashaka@gmail.com

@yashaka

Thank You