Python + Selenium: Page Object and Page Factory

基于种种原因,简单地对Python + Selenium的UI自动化做了一个简单的搭建。一些简单的分层:

  • conf: 一些配置文件,如网址,Browser Type
  • data: 数据驱动的数据
  • pages: Page对象
  • report: HTML报告,Screenshot
  • tests: 脚本
  • utils: 公共的方法,封闭Selenium,处理csv

python_selenium_page_object_example

Page Object

  1. 做了一个BasicPage类,对Selenium做了简单封装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class BasicPage(object):

    def __init__(self, driver):
    self.driver = driver

    def open_page(self, url):
    """**Description**::
    open the page
    """

    self.driver.get(url)
    self.driver.maximize_window()

    def input_text(self, locator, value):
    """**Description**::
    Fill value to the element located by locator

    :param locator:The locator to locate the element.
    :param value:value to set to the element
    :return:
    """

    element = self._find_web_element(locator)
    element.send_keys(value)
    return self
  2. Page Object实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from utils.page import *


    class BaiduPage(BasicPage):

    search_box = ('id', 'kw')
    search_button = ('id', 'su')

    def search(self, keywords):
    self.input_text(self.search_box, keywords)
    self.click_element(self.search_button)
  3. 对Browser进行封装,利用Pytest Fixture实现每个脚本运行前运行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    from selenium import webdriver
    import pytest
    from conf.const import Const


    @pytest.fixture(scope='function', params=[Const.browser])
    def start_browser(request):
    """**Description**::
    start browser
    """

    name = request.param

    try:
    if name == "firefox" or name == "Firefox" or name == "ff":
    print("start browser name :Firefox")
    driver = webdriver.Firefox()
    return driver
    elif name == "chrome" or name == "Chrome":
    print("start browser name :Chrome")
    driver = webdriver.Chrome()
    return driver
    elif name == "ie" or name == "Ie":
    print("start browser name :Ie")
    driver = webdriver.Ie()
    return driver
    elif name == "phantomjs" or name == "Phantomjs":
    print("start browser name :phantomjs")
    driver = webdriver.PhantomJS()
    return driver
    else:
    print("Not found this browser,You can use 'firefox', 'chrome', 'ie' or 'phantomjs'")
    except Exception as msg:
    print("Couldn't start browser:%s" % str(msg))
  4. 脚本层实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from pages.page_object import *
    from conf.const import *


    class TestPO():
    def test_baidu(self, start_browser):
    baidu = BaiduPage(start_browser)
    baidu.open_page(URL.baidu)
    baidu.search("selenium")
    start_browser.close()

Page Factory

Python跟Java不一样,没有专门的方法来实现,在网上看到有人做了个实现,只是在这里做了个简单的实验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
__all__ = ['cacheable', 'callable_find_by', 'property_find_by']


def cacheable_decorator(lookup):
def func(self):
if not hasattr(self, '_elements_cache'):
self._elements_cache = {} # {callable_id: element(s)}
cache = self._elements_cache

key = id(lookup)
if key not in cache:
cache[key] = lookup(self)
return cache[key]

return func


cacheable = cacheable_decorator

_strategy_kwargs = ['id_', 'xpath', 'link_text', 'partial_link_text',
'name', 'tag_name', 'class_name', 'css_selector']


def _callable_find_by(how, using, multiple, cacheable, context, driver_attr, **kwargs):
def func(self):
# context - driver or a certain element
if context:
ctx = context() if callable(context) else context.__get__(self) # or property
else:
ctx = getattr(self, driver_attr)

# 'how' AND 'using' take precedence over keyword arguments
if how and using:
lookup = ctx.find_elements if multiple else ctx.find_element
return lookup(how, using)

if len(kwargs) != 1 or list(kwargs.keys())[0] not in _strategy_kwargs:
raise ValueError(
"If 'how' AND 'using' are not specified, one and only one of the following "
"valid keyword arguments should be provided: %s." % _strategy_kwargs)

key = list(kwargs.keys())[0];
value = kwargs[key]
suffix = key[:-1] if key.endswith('_') else key # find_element(s)_by_xxx
prefix = 'find_elements_by' if multiple else 'find_element_by'
lookup = getattr(ctx, '%s_%s' % (prefix, suffix))
return lookup(value)

return cacheable_decorator(func) if cacheable else func


def callable_find_by(how=None, using=None, multiple=False, cacheable=False, context=None, driver_attr='driver',
**kwargs):

return _callable_find_by(how, using, multiple, cacheable, context, driver_attr, **kwargs)


def property_find_by(how=None, using=None, multiple=False, cacheable=False, context=None, driver_attr='driver',
**kwargs):

return property(_callable_find_by(how, using, multiple, cacheable, context, driver_attr, **kwargs))

Page:

1
2
3
4
5
6
7
8
9
10
11
12
from utils.page import *
from utils.pageobject_support import callable_find_by as by


class BaiduPage(BasicPage):

search_box = by(id_="kw")
search_button = by(id_='su')

def search(self, keywords):
self.search_box().send_keys(keywords)
self.search_button().click()

唐胡璐 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
分享创造价值,您的支持将鼓励我继续前行!