魔法
从定义了类的用户名和密码属性的两行开始,事情开始变得真的很有趣。请注意,我们定义它们为非字符串或整数,而是作为描述符号(descriptor)对象。简单地说,一个描述符是有它的get,set和delete行为改写的对象。可改写是Python页面对象的魔力。
from pageobjects import selenium_server_connection # class BasePageElement(object): # def __get__(self, obj, cls=None): selenium_server_connection.get_text(self.locator) # def __delete__(self, obj): pass
在BasePageElement类中,在应用程序中最为突出的是运用所有字段的__get__。为此目的,我们假设本应用中大部分是文本字段。
警告:如果你要尝试对一个非文本字段的描述符进行get操作,你必须在特定元素类定义__get__行为。
正如set作为一个元素的行为是与浏览器进行交互并插入文本到页面中,get的行为就是从页面读取文本。
最后所需的一点来实施此模式的基础构造是将可能需要与服务器相连的对象与Selenium服务器的连接。最好首先通过使用目前定义的页面对象的PyUnit脚本来说明。
from pageobjects.login import LoginPageObject from pageobjects import selenium_server_connection # import sys, unittest, re, time, os.path, logging # class PageObjectExample(unittest.TestCase): # def setUp(self): self.log = logging.getLogger("pragmatic.pageobjectexample") self.verificationErrors = [] self.selenium = selenium_server_connection.connect("localhost", 4444, "*chrome", "http://some.test.site") self.selenium.start() # def testLogin(self) lpo = LoginPageObject(self.selenium) lpo.username = "adam@element34.ca" lpo.password = "password" lpo.submit() # def tearDown(self): self.selenium.stop() self.assertEqual([], self.verificationErrors) # if __name__ == "__main__": unittest.main()
这是一个非常标准的 Selenium/pyunit的脚本,除了在访问Selenium服务器的过程中会创建一个单独的本地文件在包中。
为了实现这一点有两处代码,pageobjects以及__ init__.py:
from pageobjects.seleniumwrapper import SeleniumWrapper # selenium_server_connection = SeleniumWrapper()
而在pageobjects/ seleniumwrapper.py,实际连接的建立被掩盖。
from selenium import selenium # class SeleniumWrapper(object): # singleton _instance = None # def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(SeleniumWrapper, cls).__new__(cls, *args, **kwargs) return cls._instance # def connect(self, host, port, browser, server): self.connection = selenium(host, port, browser, server) return self.connection
将这一切安排妥当后,让我们将所有的代码捆绑在一起并轻松地对这个脚本进行执行。
按步进行
- Python解释器解析文件,检查语法错误,导入了一堆东西到工作区,并试着创建具有unittest.TestCase的作为超类的PageObjectExample类。
- 这也决定了当程序开始执行时unittest.main()是应该先被执行的。
- PyUnit框架扫描TestCase类的文件,找到他们后看他们是否有开头为“test.”的任何方法。
- 由于PageObjectExample有TestLogIn,随后它就执行安装方法,做了一些事情,但最重要的是使用我们封装的Selenium服务器的连接来与运行的Selenium RC服务器进行通信。
- 然后实际的测试方法被调用时,第一行代码创建了一个LoginPageObject,后续带来了创建UsernameElement和PasswordElement对象。
- 接下来的两行触发了UsernameElement和PassswordElement对象的__set__方法进入页面来输入用户名和密码。
- 该页面随后被提交。
- 如果这是一个真正的脚本,会有一些断言,并到数据库中进行检查,以确保如预期一般运行。
- 现在,测试手段完成,执行tearDown方法。
- 最后,由于没有其他的方法有前缀“test”,结果被显示给用户。
页面对象肯定是在UI层面管理测试维护成本的一种渐露头角的方式,但有一个与之相关的重大的技术成本。对于刚开始编程的人们来说,我可能还是建议他们首先抽象成函数,而不是对象。但对于有了一定了解和认识的人来说,或者如果你在自动化不断发生颠覆性变化的应用程序,那么页面对象绝对是一个值得考虑的模式。
上篇: 更加健壮的自动化脚本—Python页面对象(一)【英文原文:https://pragprog.com/magazines/2010-08/page-objects-in-python】
{测试窝原创译文,译者:大头}
译者简介:大头,在读日本九州大学修士,计算机专业,主研究方向为文本挖掘,及自然语言处理。