Upload
jeremy-kao
View
45
Download
0
Embed Size (px)
Citation preview
PyUIA 0.3Jeremy Kao / KKBOX SQA / Jun 23, 2015
Agenda
● Single Keyword Library● Current Page Tracking● UI Mapping, Page Factory Pattern and
Caching● Handlers Rotation and API Changes● Q&A
Abstraction of Layers
Single Keyword Library
As of v0.3 Prior to v0.3
Less Keyword Conflicts Multiple keywords with name 'Do Something' found.
Give the full name of the keyword you want to use.
Found: 'ThatPage.Do Something' and 'ThisPage.Do Something'
Prior to PyUIA 0.3 - Multiple Libraries
class ThisPage(MyAppLibrary):
_page_class_ios = myapp.ios.ThisPage
_page_class_android = myapp.android.ThatPage
def should_be_on_this_page(self):
self._page_object.wait_for_page_loaded()
def do_something(self):
self._page_object.do_something(target)
class ThatPage(MyAppLibrary):
_page_class_ios = myapp.ios.ThatPage
_page_class_android = myapp.android.ThatPage
def should_be_on_that_page(self):
self._page_object.wait_for_page_loaded()
def do_something(self):
self._page_object.do_something(target)
As of PyUIA 0.3 - Single Library
class MyApp(BaseAppLibrary):
_page_class_ios = ios.ThisPage
_page_class_android = android.ThisPage
def do_something(self):
# for all pages that implement this operation/protocol.
self._page_object.do_something()
return self._current_page.do_something() # return the next page
Bootstrap - Wait For <INIT> Page
class MyApp(BaseAppLibrary):
def wait_for_welcome_page(self):
context = self._current_context
if context.platform == 'android':
from myapp.android import WelcomePage
else:
from myapp.ios import WelcomePage
return get_page_object(WelcomePage, context).wait_for_page_loaded()
Current Page Tracking
● this._current_page always points to current page (of the current device).
● PyUIA updates the current page when a keyword returns a page object.
● The current page determines the runtime behavior of a keyword.
Thanks to Chloe Chen for the idea.
Current Page == Implicit SubjectCurrent page is an analogue to the implied subject in an imperative sentence.
# KeywordsWait For Home PageGo To SettingsEnable SynchronizationGo Back
# Page Objects (Python)
home = HomePage(driver).
wait_for_page_loaded()
settings = home.go_to_settings()
settings.enable_synchronization()
settings.go_back() # Home
PageFactory Impl. in Python?Refer to PageFactory (Java)...● PageFactory implementation from selenium for python - Stack Overflow
(Nov 8, 2013)● Does Webdriver support pagefactory for Python? - Stack Overflow (Dec
12, 2012)● PageFactory Implementation in Python - Google Groups (May 8, 2012)● PageFactory implementation in Python? - Google Groups (Aug 11, 2011)
Unfortunately, we have to invent the wheel ~ Implementing PageFactory Pattern in Python (June 10, 2015) (find_by, @cacheable)
Google Search Pagefrom pyuia.selenium import SeleniumPageObject as PageObject, find_by, cacheable
from selenium.webdriver.common.by.By
class GoogleSearchPage(PageObject):
_search_box = find_by(how=By.NAME, using='q')
_search_button = find_by(name='btnK')
def search(self, keywords):
self._search_box().click()
self._search_box().send_keys(keywords) # look up twice?
self._search_button().click()
The following keyword arguments are supported for various locator strategies: id_ (to avoid conflict with the built-in keyword id), name, class_name, css_selector, tag_name, xpath, link_text, partial_link_text.
find_bydef find_by(how=None, using=None, multiple=False, cacheable=True, if_exists=False,
context=None, driver_attr='_driver', **kwargs):
● multiple – the answer to @FindBy for a list of elements.● cacheable – the answer to @CacheLookup. (see also
@cacheable for customized lookup.)● if_exists - for conditional UI elements. ● context – the answer to @FindBys.● driver_attr – the attribute name for getting the
reference to WebDriver. Defaults to ’_driver’.
find_by (cont.)# prior to v0.3
def _clickme_button(self):
return self._driver.find_element_by_name('Click Me!')
def _clickme_button(self):
return self._driver.find_element_by_id('click_me')
# as of v0.3
_clickme_button = find_by(name='Click Me!')
_clickme_button = find_by(id_='click_me')
find_by (cont.)
# prior to v0.3
def _products(self):
container = self._driver.find_element_by_id('products_list')
return container.find_elements_by_class_name('android.widget.TextView')
# as of v0.3
_products = find_by(class_name='android.widget.TextView', multiple=True,
context=find_by(id_='products_list'))
find_by (cont.)
# prior to v0.3
def _tip_if_exists(self):
try:
return self._driver_find_element_by_id('tip')
except NoSuchElementException:
return None
# as of v0.3
_tip_if_exists = find_by(id_='tip', if_exists=True)
@cacheable (for customized lookup)
@cacheable
def _categories(self):
candidates = self._driver.find_elements_by_id('list')
assert len(candidates) == 2, candidates
container = candidates[1] # the 2nd one
return container.find_elements_by_class_name('android.widget.TextView')
Handlers Rotation def assert_on_this_page(self):
self._assert_any_present(
[self._lyrics_button, self._discovery_tutorial_view_if_exists],
handlers={
self._other_device_playing_alert: self._dismiss_devices_alert, # H1
self._storage_space_alert: self._dismiss_storage_alert, # H2
self._high_quality_prompt_message: self._dismiss_high_quality_prompt # H3
})
# ...
● Prior to v0.3 - (H1 ➔ H2 ➔ H3) ➥ (H1 ➔ H2 ➔ H3) ...● As of v0.3 - H1 ➥ H2 ➥ H3 ➥ H1 ➥ H3 ➥ H1 ➥ H1 ...
Modeling: Page and UI Fragment
MainPage
Menu
ActionBarclass Menu(class):
def open_menu(self): pass
class ActionBar(class):
def title(self): pass
class MainPage(AppiumPO, Menu, ActionBar): pass
Page-to-page Transitions
A(WelcomePage)
_go_to(page_class)
_back_to(page_class)
B(MainPage)
● assert_on_this_page(from_page_class) - assertion, no waiting● _get_page_entry_handlers(from_page_class) - rotation● _on_page_entry(from_page_class) - replace _wait_for_idle.
Page-to-page Transitions (cont.)
# Page A
_clickme_button = find_by(id_='click_me')
def go_to_b(self):
self._clickme_button().click()
return self._go_to(BPage)
# Page B
_back_button = find_by(name='Back')
def go_back(self):
self._back_button().click()
# go to the previous page by default
return self._back_to()
_go_to(page_class)
_back_to(page_class=None)
Page-to-page Transitions (cont.)# WelcomePage
def assert_on_this_page(self, from_page_class):
self._assert_present(self._start_button)
def start(self):
self._start_button().click() # look up again?
self._go_to(MainPage)
Page-to-page Transitions (cont.)
# MainPage
def assert_on_this_page(self, from_page_class):
self._assert_present(self._main_menu)
def _get_page_entry_handlers(self, from_page_class):
if from_page_class == WelcomePage:
return { self._network_error: self._retry }
Page-to-page Transitions (cont.)# MainPage
def _on_page_entry(self, from_page_class):
if from_page_class != WelcomePage: return
self._wait_disappear(self._loading_indicator)
self._watch({
self._chromecast_ok_button: self._dismiss_chromecast_tip,
self._tutorial_ok_button: self._dismiss_tutorial,
}, 10) # max duration
return True # indicates UI changed (takes another screenshot)
Where _watch (handlers, max_duration) replaces _handle_conditional_views (handlers, duration)
References
● PyUIA● Page Object Pattern (Martin Fowler)● PageFactory pattern (Python impl.)● 利用 Appium + Robot Framework 實現跨平
台 App 互動測試 (Nov 14, 2014)
Q&AThanks you all for listening ~