View
1.574
Download
9
Category
Preview:
Citation preview
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
Recommended