po模式

297 阅读4分钟

PO模型是UI测试自动化过程中非常频繁使用的一种设计模式,使用这种模式,可以有效地提高代码的重用性,并且让测试自动化的代码维护更加方便。PO模式是页面对象模型(POM)的全称,有时也称为页面对象模式。该模式首先由Martin fowler提出,正推动着测试自动化框架的发展,成为一种非常主流的测试自动化设计模式。在PO模式下,每个UI页面使用编程语言类。在本类中,通过函数形式定义了页面的行为和操作。这让调用者不需要注意执行的具体操作确切是点击和拖动,而是专注于具体的业务,如登录、购物等,即使程序员直接将代码读给产品经理,他也能读懂。

没有使用po模式时

在测试用例中编写浏览器API的操作,对于代码编写者并没有多大难度,因为他已经非常熟悉这些API了,但是浏览器并没有体现业务的操作,至少没有像产品经理那样熟悉,所以他很难和产品经理沟通,也很难沟通和开发,甚至半个月后,他已经忘记了自己写的东西。

def test_login_mail(self): driver = self.driver driver.get("www.xxx.xxx.com") driver.find_element_by_id("idInput").clear() driver.find_element_by_id("xxxxxxx").send_keys("xxxxx") driver.find_element_by_id("xxxxxxx").clear() driver.find_element_by_id("xxxxxxx").send_keys("xxxxxx") driver.find_element_by_id("loginBtn").click()

使用PO模式

有利于梳理业务,也有利于与他人沟通。当你把下面的代码交给产品经理时,他大概知道你度量的是什么业务,可以帮助你纠正你的测试过程是否正确,或者给出一些更有建设性的意见,这对于需要频繁沟通的大型项目和业务是非常有用的。

def test_login_mail(self): LoginPage(driver).login()

而浏览器本身的操作,可以分成一个更底部的模块,代码可以把你右边的调用者暴露出来,一个产品经理并不关心你这个页面的元素定位是什么,他也不懂。

class LoginPage: username_loc=(By.ID,"idInput") password_loc =(By.ID,"pwdInput") submit_loc =(By.ID,"loginBtn") span_loc=(By.CSS_SELECTOR,"div.error-tt>p") dynpw_loc =(By.ID,"lbDynPw") userid_loc =(By.ID,"spnUid")

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

def login(self): self.driver.find_element(*self.username_loc).clear() self.driver.find_element(*self.username_loc).send_keys("xxxxx") self.driver.find_element(*self.password_loc).clear() self.driver.find_element(*self.password_loc).send_keys("xxxxxx") self.driver.find_element(*self.submit_loc).click()

这种方式的元素定位方式分离。但表达的元素定位可读性不是很强,可以用一种属性的方式表示元素,将所有元素统一在一起,并且更方便修改。

class LoginPage: def init(self, driver) self.driver = driver

@property def username_element(self): return self.driver.find_element('id', 'idInput')

@property def password_element(self): return self.driver.find_element('id', 'pwdInput')

@property def submit_element(self): return self.driver.find_element('id', 'loginBtn')

def login(self, name, password): self.username_element.send_keys(name) self.password_element.send_keys(password) self.submit_element.click()

第三种方式可以充分利用Python的描述符属性,您会发现许多序列化库或ORM框架都有类似的用法。

class LoginPage: def init(self, driver) self.driver = driver

username = Element(css='#idInput', desc='用户名输入框') password = Element(css='#pwdInput', desc='密码输入框') confirm = Element(css='#loginBtn', desc='登录确认按钮')

def login(self, name, password): self.username.send_keys(name) self.password.send_keys(password) self.confirm.click()

而element类可以通过Python描述符实现,这里为方便起见,只定义xpath元素定位方法:

class Element:

def init(self,xpath=None,desc=''): self.xpath = xpath self.desc = desc

def get(self, instance, owner): driver = instance.browser el = driver.find_element('xpath', self.xpath) return el

# PO模式和DDD

PO模式是DDD(域驱动设计)的一个简单实现,但还不够彻底。如果您想在测试自动化中实现DDD,我认为还有一些空间可以优化。首先,一个企业可能不只是单个页面的操作,比如登录可能不只是涉及到LoginPage这个页面,所以直接在LoginPage中进行。写登陆功能不是很合理。对于调用者,应该在登录时指定调用者,而不是引用页面。像这样的:

user.login()

or

login(user)

我们把代码写成自然语言,任何懂英语的人都知道代码在做什么,在DDD中,称为领域特定语言(DSL),为了实现这个逻辑,在页面和调用应该的中间会有一个层次结构来封装用户。

其次,Page Page将依赖于底层资源,如组件、元素类型。所以在Page类的底部应该使用InputElement, ButtonElement,元素类和HeaderComponent SelectElement样本。FooterComponent这样的组件类。

class LoginPage: username_filed = InputElement('xxx') password_filed = PasswordElement('xxx')

梳理域驱动设计对于大型项目,同步,业务,通信,非常有帮助,是一种以业务为中心的设计范式。PO模型适用于DDD的小规模应用,以及足够具体的好处:易于维护。每一页的操作都单独放在一个类文件中,当前结束页被修改时,只需要找到相应的类文件修改,其他代码不需要修改,它符合单一责任原则。容易重用。在自动化测试中,一个测试包含多个测试步骤,这些测试步骤可能涉及多个页面。并且操作可能在用例和用例之间重叠。PO模式可以重复使用这些测试步骤,简化代码的编写。提高了可读性。页面的操作封装在函数的形式中。函数名将是注释的作用,其他阅读代码的人可以通过函数了解业务。