KISS Automation.py
-
Upload
yasha-kramarenko -
Category
Technology
-
view
47 -
download
0
Transcript of KISS Automation.py
AfterwordsThere are good practices in context,
but there are no best practices.
(c) Cem Kaner, James Bach
Afterwords PrefaceThere are good practices in context,
but there are no best practices.
(c) Cem Kaner, James Bach
KISS?
Keep It Simple Stupid!
Web UI Automation…
Selenium vs Wrappers
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): driver.get("http: //google.com/ncr") driver.find_element_by_name('q').send_keys('selenide' + Keys.ENTER) WebDriverWait(driver, 4).until( text_to_be_present_in_element( (By.CSS_SELECTOR, '.srg>.g:nth-of-type(1)'), 'Selenide: concise UI tests in Python'))
Wait for list nth element text
Wait for list nth element text
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): visit('http: //google.com/ncr') s(by_name('q')).set('selenide').press_enter() ss('.srg>.g')[0].should_have( text('Selenide: concise UI tests in Python'))
Selene
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): visit('http: //google.com/ncr') s(by_name('q')).set('selenide').press_enter() ss('.srg>.g')[0].should_have(text('Selenide: concise UI tests in Python'))
Wait for list nth element textSelene
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): visit('http: //google.com/ncr') s(by_name('q')).set('selenide').press_enter() ss('.srg>.g')[0].should_have(text('Selenide: concise UI tests in Python'))
Wait for list nth element textSelene
readable explicit wait
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): visit('http: //google.com/ncr') s(by_name('q')).set('selenide').press_enter() ss('.srg>.g')[0].should_have(text('Selenide: concise UI tests in Python'))
Wait for list nth element textSelene
built in implicit wait for visible nth element
# WebDriverWait(driver, 4).until(# text_to_be_present_in_element(# (By.CSS_SELECTOR, '.srg>.g:nth-of-type(1)'),## > 'Selenide: concise UI tests in Python'))# selenium_wait_for_list_nth_element_text.py:31:# ...# E TimeoutException: Message:# E Stacktrace:# ...
Wait error message
?
# ...# E TimeoutException: Message:# E failed while waiting 4 seconds# E to assert text# E for element found by: ('selene', "('css selector', '.srg>.g')[0]"):# E expected: Selenide: concise UI tests in Python# E actual: Selenide: concise UI tests in Java# E selenide.org/# E "Selenide is really nice and capable tool …
Wait error messageSelene
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): visit('http: //google.com/ncr') s(by_name('q')).set('selenide').press_enter() ss('.srg>.g')[0].should_have(text('Selenide: concise UI tests in Python'))
PageObject support !Selene
can be naturally extracted to page object fields
PageObject support !Selene
class Google(object): def __init__(self): self.search = s(by_name('q')) self.results = ss(‘.srg>.g')def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): google = Google() visit('http: //google.com/ncr') google.search.set('selenide').press_enter() google.results[0].should_have(text('Selenide: concise UI tests in Python'))
PageObject support ?
def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): driver.get("http: //google.com/ncr") driver.find_element_by_name('q').send_keys('selenide' + Keys.ENTER) WebDriverWait(driver, 4).until( text_to_be_present_in_element( (By.CSS_SELECTOR, '.srg>.g:nth-of-type(1)'), 'Selenide: concise UI tests in Python'))
class Google(object): def __init__(self, driver): self.driver = driver self.firstResult = (By.CSS_SELECTOR, '.srg>.g:nth-of-type'(1)') #self.secondResult ... o_O def search(self): return self.driver.find_element_by_name('q')def test_google_fails_to_find_selenide_org_in_top_by_wrong_text(): google = Google(driver) driver.get("http: //google.com/ncr") google.search().send_keys('selenide' + Keys.ENTER) WebDriverWait(driver, 4).until( text_to_be_present_in_element(google.firstResult, 'Selenide: concise UI tests in Python'))
PageObject support ?Unnaturally extracted…
ss('.srg>.g')[0].should_have( text('Selenide: concise UI tests in Python'))
vs
WrappersSelenium
Pure Test logicTest logic messed up with tech details
WebDriverWait(driver, 4).until( text_to_be_present_in_element( (By.CSS_SELECTOR, '.srg>.g:nth-of-type(1)'), 'Selenide: concise UI tests in Python'))
ss('.srg>.g')[0].should_have( text('Selenide: concise UI tests in Python'))
vs
WrappersSelenium
all inclusive ;)• useless implicit waits • non informative messages of explicit waits • lack of PageObject support
WebDriverWait(driver, 4).until( text_to_be_present_in_element( (By.CSS_SELECTOR, '.srg>.g:nth-of-type(1)'), 'Selenide: concise UI tests in Python'))
ss('.srg>.g')[0].should_have( text('Selenide: concise UI tests in Python'))
vs
WrappersSeleniumWebDriverWait(driver, 4).until( text_to_be_present_in_element( (By.CSS_SELECTOR, '.srg>.g:nth-of-type(1)'), 'Selenide: concise UI tests in Python'))
may be SlowerFast
XPath vs CSS
vs
CSSXPATH
//*[@id='concise']
<div id='concise'>...</div>
#concise
vs
CSSXPATH
//*[contains(concat(' ', normalize-space(@class), ' '), ' readable ')]
<div class='concise readable'>...</div>
.readable
vs
CSSXPATH
//*[.='Power']
<div>Power</div>
?
vs
CSSXPath
- bulky
- complicated
+ more powerful
+ concise
+ readable
+ easy & simple
XPath + Selenium vs
CSS + Wrappers
vs
readable, in case of error will show “failed” part
driver.find_element_by_xpath("//*[@id='todo-list']//li[.//text()='4']//*[@class='destroy']").click()
ss("#todo-list li").find_by(exact_text("4")).find(".destroy").click()
crazy, in case of error you don’t know which part is wrong
integral
broken down
vs
CSS + WrappersXPath
- bulky
- non informative
+ powerful
- complicated
+ concise
+ readable
+ powerful
+ informative
+ easy & simple
End to End vs Atomic
def test_task_life_cycle(): given_at_todomvc() add("a") edit("a", "a edited") toggle("a edited") filter_active() assert_no_tasks() filter_completed() delete("a edited") assert_no_tasks() # ...
End to End Scenario
implicit checks
def test_add(): given_at_todomvc() add("a", "b") assert_tasks("a", "b") assert_items_left(2)
Atomic tests styledef test_delete(): given_at_todomvc("a", "b", "c") delete("b") assert_tasks("a", "c") assert_items_left(2)
def test_edit(): given_at_todomvc("a", "b", "c") edit("c", "c edited") assert_tasks("a", "b", "c edited") assert_items_left(3)
def test_toggle(): # ...
# ...
vs
AtomicE2E
SimpleEasy
def test_task_life_cycle(): given_at_todomvc() add("a") edit("a", "a edited") toggle("a edited") filter_active() assert_no_tasks() filter_completed() delete("a edited") assert_no_tasks() # ...
def test_add(): given_at_todomvc() add("a", "b") assert_tasks("a", "b") assert_items_left(2)
def test_delete(): # ...
# ...
Simple is not Easy
vs
AtomicE2E
SimpleEasy
+ more coverage
+ in less time
+ in case of bugs, gives more complete report
+ easier to identify reason from the report
=>
+ less time and efforts in support
+ with less efforts during POC implementation
+ integration coverage
vs Simple“Pretty”
Reports
public class TodoMVCTest { @Test public void testTaskLifeCycle(){ givenAtTodoMVC(); add("a"); toggle("a"); filterActive(); assertNoTasks(); filterCompleted(); edit("a", "a edited"); toggle("a edited"); assertNoTasks(); // ... }
@Test public void testAddTasks(){ givenAtTodoMVC(); add("a", "b", "c"); assertTasks("a", "b", "c"); assertItemsLeft(3); } @Test public void testDeleteTask(){ givenAtTodoMVC("a", "b", "c"); delete("b"); assertTasks("a", "c"); assertItemsLeft(2); } // ...}
Simple
<plugin> <groupId>org.apache.maven.plugins </groupId> <artifactId>maven-surefire-plugin </artifactId> <version>2.18.1 </version> <configuration> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" </argLine> <properties> <property> <name>listener </name> <value>ru.yandex.qatools.allure.junit.AllureRunListener </value> </property> </properties> </configuration> <dependencies> <dependency> <groupId>org.aspectj </groupId> <artifactId>aspectjweaver </artifactId> <version>${aspectj.version} </version> </dependency> </dependencies> </plugin>
Allure reporting
<reporting> <excludeDefaults>true </excludeDefaults> <plugins> <plugin> <groupId>ru.yandex.qatools.allure </groupId> <artifactId>allure-maven-plugin </artifactId> <version>2.0 </version> </plugin> </plugins></reporting>
@Steppublic static void add(String ... taskTexts) { for(String text: taskTexts){ newTodo.setValue(text).pressEnter(); }}@Steppublic static void filterActive(){ $(By.linkText("Active")).click();}
// ...
Allure reporting
“Pretty” with Allure
vs
SimplePretty
SimpleMore configs & annotations
Reporting
vs
SimplePretty
Enough for “Atomic”Good for E2E
Reporting
BDD vs xUnit
BDD
vs
xUnitBDD
Simple & Easy? :)
1. Write text scenarios with steps
2. Define steps
2.1 Introduce state
3. Map steps
4. Configure Runner
5. Run
6. Get pretty reports
1. Write tests with steps
2. Define steps
3. Run
4. Get reportsstill pretty with Allure
vs
xUnitBDD
Less coding, Full power of PLNo vars & return from steps
1. Write tests with steps
2. Define steps
3. Run
4. Get reportsstill pretty with Allure
vs
xUnitBDD
Less coding, Full power of PLMonkeys-friendly
1. Write tests with steps
2. Define steps
3. Run
4. Get reportsstill pretty with Allure
THEN("Adds one more column for data and fill its cell");app.table().addColumnAfter(0, "login valid?");app.table().row(0).cell(1).fill("true");app.table().shouldHaveRows( asList("1", "true"));THEN("Adds one more row with both connected data storage data and new data");menu = app.table().row(0).cell(0).menu();menu.open();menu.select("Insert row below”); app.table().row(1).fill("2", "false");app.table().shouldHaveRows( asList("1", "true"), asList("2", "false"));
xUnit test with PageObject
THEN("Adds one more column for data and fill its cell");app.table().addColumnAfter(0, "login valid?");app.table().row(0).cell(1).fill("true");app.table().shouldHaveRows( asList("1", "true"));THEN("Adds one more row with both connected data storage data and new data");menu = app.table().row(0).cell(0).menu();menu.open();menu.select("Insert row below”); app.table().row(1).fill("2", "false");app.table().shouldHaveRows( asList("1", "true"), asList("2", "false"));
do you really need an additional BDD
layer over it?
Allure GIVEN/WHEN/THEN helpers for additional reporting
PageModules
vsModularOOP
PageObjects
from pages import tasks
def test_filter_tasks():
tasks.visit() tasks.add("a", "b", "c") tasks.should_be("a", "b", "c") ...
from pages.tasks import TasksPage
def test_filter_tasks(): tasks = TasksPage() tasks.visit() tasks.add("a", "b", "c") tasks.should_be("a", "b", "c") ...
PageModulesPageObjects
ModularOOP
class TasksPage: def __init__(self): self.tasks = ss("#todo-list>li") def visit(self): tools.visit('https: //todomvc4tasj.herokuapp.com/') ... def should_be(self, *task_texts): self.tasks.filterBy(visible).should_have(exact_texts(*task_texts)) ...
PageObjects
OOP
#tasks.py
tasks = ss("#todo-list>li")def visit(): tools.visit('https: //todomvc4tasj.herokuapp.com/')
...
def should_be(*task_texts): tasks.filterBy(visible).should_have(exact_texts(*task_texts))
...
PageModules
Modular
PageModules
vs
ModularOOP
PageObjects
+ simple
+ easy
+ “newbies” friendly (in context of
implementation)
+ parallelised tests (where there is no automatic
driver management per thread)
+ two browsers in one test
AfterwordsThere are good practices in context,
but there are no best practices.
(c) Cem Kaner, James Bach
Q&A
Thank you!
@yashakaautomician.com
github.com/yashaka facebook/yashaka
twitter.com/yashaka [email protected]
seleniumcourses.com
12.2016