Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

KISS Automation.py

214 Aufrufe

Veröffentlicht am

These are the slides for the talk given at https://www.meetup.com/South-Florida-Software-Testing/events/233980212

Short summary:
KISS (Keep It Simple Stupid) techniques and practices in Web UI Automation on topics:
- selenium webdriver vs wrappers
- XPath vs CSS Selectors + Selene
- End to End vs Atomic tests
- Pretty vs Simple reports
- BDD vs XUnit style of tests
- PageObject vs PageModules (OOP vs Procedural/Modular programming)

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

KISS Automation.py

  1. 1. KISS_Automation.py @yashakaautomician.com 12.2016
  2. 2. Afterwords There are good practices in context, but there are no best practices. (c) Cem Kaner, James Bach
  3. 3. Afterwords Preface There are good practices in context, but there are no best practices. (c) Cem Kaner, James Bach
  4. 4. KISS?
  5. 5. Keep It Simple Stupid!
  6. 6. Web UI Automation…
  7. 7. Selenium vs Wrappers
  8. 8. 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
  9. 9. 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
  10. 10. 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 text Selene
  11. 11. 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 text Selene readable explicit wait
  12. 12. 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 text Selene built in implicit wait for visible nth element
  13. 13. # 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 ?
  14. 14. # ...
 # 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 message Selene
  15. 15. 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
  16. 16. 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'))
  17. 17. 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'))
  18. 18. 
 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…
  19. 19. ss('.srg>.g')[0].should_have(
 text('Selenide: concise UI tests in Python')) vs WrappersSelenium Pure Test logic Test 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'))
  20. 20. 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'))
  21. 21. ss('.srg>.g')[0].should_have(
 text('Selenide: concise UI tests in Python')) vs WrappersSelenium 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')) may be SlowerFast
  22. 22. XPath vs CSS
  23. 23. vs CSSXPATH //*[@id='concise'] <div id='concise'>...</div> #concise
  24. 24. vs CSSXPATH //*[contains(concat(' ', normalize- space(@class), ' '), ' readable ')] <div class='concise readable'>...</div> .readable
  25. 25. vs CSSXPATH //*[.='Power'] <div>Power</div> ?
  26. 26. vs CSSXPath - bulky - complicated + more powerful + concise + readable + easy & simple
  27. 27. XPath + Selenium vs CSS + Wrappers
  28. 28. 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
  29. 29. vs CSS + WrappersXPath - bulky - non informative + powerful - complicated + concise + readable + powerful + informative + easy & simple
  30. 30. End to End vs Atomic
  31. 31. 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
  32. 32. def test_add():
 given_at_todomvc()
 add("a", "b")
 assert_tasks("a", "b") assert_items_left(2) Atomic tests style def 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():
 # ... # ...
  33. 33. 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():
 # ... # ...
  34. 34. Simple is not Easy
  35. 35. –Rich Hickey “Simple Made Easy”
  36. 36. 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
  37. 37. vs Simple“Pretty” Reports
  38. 38. 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);
 }
 // ...
 }
  39. 39. Simple
  40. 40. <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>
  41. 41. @Step
 public static void add(String ... taskTexts) {
 for(String text: taskTexts){
 newTodo.setValue(text).pressEnter();
 }
 }
 
 @Step
 public static void filterActive(){
 $(By.linkText("Active")).click();
 }
 // ... Allure reporting
  42. 42. “Pretty” with Allure
  43. 43. vs SimplePretty SimpleMore configs & annotations Reporting
  44. 44. vs SimplePretty Enough for “Atomic” Good for E2E Reporting
  45. 45. BDD vs xUnit
  46. 46. BDD
  47. 47. 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 reports still pretty with Allure
  48. 48. vs xUnitBDD Less coding, Full power of PL No vars & return from steps 1. Write tests with steps 2. Define steps 3. Run 4. Get reports still pretty with Allure
  49. 49. vs xUnitBDD Less coding, Full power of PL Monkeys-friendly 1. Write tests with steps 2. Define steps 3. Run 4. Get reports still pretty with Allure
  50. 50. 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
  51. 51. 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
  52. 52. PageModules vs ModularOOP PageObjects
  53. 53. 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
  54. 54. 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
  55. 55. #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
  56. 56. 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
  57. 57. Afterwords There are good practices in context, but there are no best practices. (c) Cem Kaner, James Bach
  58. 58. Q&A
  59. 59. Thank you! @yashakaautomician.com github.com/yashaka facebook/yashaka twitter.com/yashaka yashaka@gmail.com seleniumcourses.com 12.2016

×