KISS Automation.py

68
KISS_Automation.py @yashaka automician.com 12.2016

Transcript of KISS Automation.py

Page 1: KISS Automation.py

KISS_Automation.py

@yashakaautomician.com 12.2016

Page 2: KISS Automation.py

AfterwordsThere are good practices in context,

but there are no best practices.

(c) Cem Kaner, James Bach

Page 3: KISS Automation.py

Afterwords PrefaceThere are good practices in context,

but there are no best practices.

(c) Cem Kaner, James Bach

Page 4: KISS Automation.py

KISS?

Page 5: KISS Automation.py

Keep It Simple Stupid!

Page 6: KISS Automation.py

Web UI Automation…

Page 7: KISS Automation.py
Page 8: KISS Automation.py

Selenium vs Wrappers

Page 9: KISS Automation.py

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

Page 10: KISS Automation.py

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

Page 11: KISS Automation.py

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

Page 12: KISS Automation.py

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

Page 13: KISS Automation.py

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

Page 14: KISS Automation.py

# 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

?

Page 15: KISS Automation.py

# ...# 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

Page 16: KISS Automation.py

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

Page 17: KISS Automation.py

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'))

Page 18: KISS Automation.py

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'))

Page 19: KISS Automation.py

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…

Page 20: KISS Automation.py

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'))

Page 21: KISS Automation.py

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'))

Page 22: KISS Automation.py

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

Page 23: KISS Automation.py
Page 24: KISS Automation.py

XPath vs CSS

Page 25: KISS Automation.py

vs

CSSXPATH

//*[@id='concise']

<div id='concise'>...</div>

#concise

Page 26: KISS Automation.py

vs

CSSXPATH

//*[contains(concat(' ', normalize-space(@class), ' '), ' readable ')]

<div class='concise readable'>...</div>

.readable

Page 27: KISS Automation.py

vs

CSSXPATH

//*[.='Power']

<div>Power</div>

?

Page 28: KISS Automation.py

vs

CSSXPath

- bulky

- complicated

+ more powerful

+ concise

+ readable

+ easy & simple

Page 29: KISS Automation.py
Page 30: KISS Automation.py

XPath + Selenium vs

CSS + Wrappers

Page 31: KISS Automation.py
Page 32: KISS Automation.py

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

Page 33: KISS Automation.py

vs

CSS + WrappersXPath

- bulky

- non informative

+ powerful

- complicated

+ concise

+ readable

+ powerful

+ informative

+ easy & simple

Page 34: KISS Automation.py
Page 35: KISS Automation.py

End to End vs Atomic

Page 36: KISS Automation.py
Page 37: KISS Automation.py

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

Page 38: KISS Automation.py

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(): # ...

# ...

Page 39: KISS Automation.py

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(): # ...

# ...

Page 40: KISS Automation.py

Simple is not Easy

Page 41: KISS Automation.py

–Rich Hickey

“Simple Made Easy”

Page 42: KISS Automation.py

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

Page 43: KISS Automation.py
Page 44: KISS Automation.py

vs Simple“Pretty”

Reports

Page 45: KISS Automation.py

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); } // ...}

Page 46: KISS Automation.py

Simple

Page 47: KISS Automation.py

<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>

Page 48: KISS Automation.py

@Steppublic static void add(String ... taskTexts) { for(String text: taskTexts){ newTodo.setValue(text).pressEnter(); }}@Steppublic static void filterActive(){ $(By.linkText("Active")).click();}

// ...

Allure reporting

Page 49: KISS Automation.py

“Pretty” with Allure

Page 50: KISS Automation.py

vs

SimplePretty

SimpleMore configs & annotations

Reporting

Page 51: KISS Automation.py

vs

SimplePretty

Enough for “Atomic”Good for E2E

Reporting

Page 52: KISS Automation.py
Page 53: KISS Automation.py

BDD vs xUnit

Page 54: KISS Automation.py

BDD

Page 55: KISS Automation.py

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

Page 56: KISS Automation.py

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

Page 57: KISS Automation.py

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

Page 58: KISS Automation.py

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

Page 59: KISS Automation.py

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

Page 60: KISS Automation.py
Page 61: KISS Automation.py

PageModules

vsModularOOP

PageObjects

Page 62: KISS Automation.py

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

Page 63: KISS Automation.py

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

Page 64: KISS Automation.py

#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

Page 65: KISS Automation.py

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

Page 66: KISS Automation.py

AfterwordsThere are good practices in context,

but there are no best practices.

(c) Cem Kaner, James Bach

Page 67: KISS Automation.py

Q&A

Page 68: KISS Automation.py

Thank you!

@yashakaautomician.com

github.com/yashaka facebook/yashaka

twitter.com/yashaka [email protected]

seleniumcourses.com

12.2016