KISS Automation.py

Post on 07-Jan-2017

47 views 0 download

Transcript of KISS Automation.py

KISS_Automation.py

@yashakaautomician.com 12.2016

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

–Rich Hickey

“Simple Made 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 yashaka@gmail.com

seleniumcourses.com

12.2016