Web自动化测试

霍格沃兹测试开发学社 ceshiren.com

目录

  • PageObject设计思想
  • PageObject 企业微信建模
  • PO自动化测试实战演练

前言

功能测试场景


autonumber

actor 测试工程师 as tester
participant 系统 as sys

tester -> sys: 测试工程师在系统页面做点击输入等操作
sys -> sys: 系统运行产生响应
sys -> tester: 测试工程师拿到实际结果与预期结果对比

UI 自动化测试场景


autonumber

actor 测试工程师 as tester
participant Python as python
participant Selenium as web
participant 被测系统 as sys

tester -> python: 使用Python编写自动化测试脚本
python -> web: 调用selenium执行自动化测试脚本
web -> sys: 模拟点击、输入等操作,并获取自动化运行的响应结果
sys -> python: Python拿到系统的响应结果,并与提前设定好的预期结果进行对比

什么时候需要做 UI 自动化测试

UI 自动化测试需要哪些技术

  • Web 自动化测试: Selenium、Cypress、Airtest
  • App 自动化测试: Appium、ATX、Airtest

Selenium

Selenium is a suite of tools to automate web browsers across many platforms. runs in many browsers and operating systems can be controlled by many programming languages and testing frameworks

官网

https://seleniumhq.github.io/selenium/docs/api/py/

PageObject设计思想

为什么需要PageObject设计模式

一个添加成员的步骤:

  1. 登录_login页面
  2. 登录后进入首页_main页面
  3. 点击添加成员_main页面
  4. 填写添加信息_add_member页面
  5. 点击保存_add_member页面
  6. 返回通讯录_add_membe页面
  7. 加断言做验证_contact页面

传统的web测试用例

def test_add_member(self):
   self.driver.get(“https://work.weixin.qq.com/wework_admin/frame#index")
   element_locator = (By.LINK_TEXT, “添加成员")
    WebDriverWait(self.driver, 10).until(expected_conditions.element_to_be_clickable(element_locator))
    self.driver.find_element(*element_locator).click()
    self.driver.find_element(By.NAME, 'username').send_keys("abc")
    self.driver.find_element(By.NAME, 'english_name').send_keys("abc")
    self.driver.find_element(By.NAME, "acctid").send_keys("abc")
    self.driver.find_element(By.CSS_SELECTOR, '.ww_telInput_zipCode_input input').click()
    self.driver.find_element(By.CSS_SELECTOR, 'li[data-value="853"]').click()
   assert...

如果需要多添加一个步骤

从首页->通讯录->添加成员应该怎么改

  1. 登录_login页面
  2. 进入首页_main页面
  3. 点击通讯录_main页面
  4. 点击添加成员_contact页面
  5. 填写添加信息_add_member页面
  6. 点击保存_add_member页面
  7. 返回通讯录_add_member页面
  8. 加断言做验证_contact页面

传统的web测试用例

对应的自动化测试代码

def test_add_member(self):
   self.driver.get(“https://work.weixin.qq.com/wework_admin/frame#index")
   element_locator = (By.LINK_TEXT, “添加成员")
    WebDriverWait(self.driver, 10).until(expected_conditions.element_to_be_clickable(element_locator))
    self.driver.find_element(*element_locator).click()
    self.driver.find_element(By.NAME, 'username').send_keys("abc")
    self.driver.find_element(By.NAME, 'english_name').send_keys("abc")
    self.driver.find_element(By.NAME, "acctid").send_keys("abc")
    self.driver.find_element(By.CSS_SELECTOR, '.ww_telInput_zipCode_input input').click()
    self.driver.find_element(By.CSS_SELECTOR, 'li[data-value="853"]').click()
   assert...

传统UI自动化测试用例的问题

  • 无法适应UI变化,UI变化会导致大量的case需要修改
  • 无法清晰表达业务用例场景
  • 大量的样板代码driver find click

PageObject原理以及六大原则

PO设计思想

设计思想

PO六大原则

一定要活学活用,不要死搬硬套

六大原则

原则解读

  • 方法意义
    • 用公共方法代表UI所提供的功能
    • 方法应该返回其他的PageObject或者返回用于断言的数据
    • 同样的行为不同的结果可以建模为不同的方法
    • 不要在方法内加断言
  • 字段意义
    • 不要暴露页面内部的元素给外部
    • 不需要建模UI内的所有元素

PageObject 企业微信建模

原型图

转换

  • 黄色的方块代表一个类
  • 每条线代表这个页面提供的方法
  • 箭头的始端为开始页面
  • 箭头的末端为跳转页面或需要断言的数据
@startuml
autonumber
participant 企业微信主页面 as main

participant 通讯录页面 as contact

participant 添加成员页面 as add_member

main -> contact: 点击通讯录

main -> add_member: 点击添加成员

contact -> add_member: 点击添加成员

add_member -> contact: 填写成员资料, 点击保存

contact -> contact: 获取成员列表断言
@enduml

PO自动化测试实战演练

实战练习思路

@startmindmap
* HowToDo
** 梳理测试用例
** 构造PO模型
*** 构造页面相关类和方法
*** 实现暂时实际为空
** 编写测试用例
*** 根据业务逻辑编写
*** 链式调用
** 填充具体实现
*** Driver初始化
*** BasePage封装
** 优化用例
*** 封装样板代码
*** 提取页面元素
*** 添加起始页的url
@endmindmap

梳理测试用例

  • 根据业务逻辑编写,添加断言
  • 通过链式调用更加方便描述业务逻辑
  • 具体的实现先设置为空

填充实现-Driver初始化、BasePage封装

  • 问题:

    • Driver初始化如果绑定在某个页面类中,那么多个页面类都需要进行初始化操作
  • 解决方案:

    • Driver初始化的部分放在BasePage中,其他Page类继承BasePage。子类可以使用父类的属性,直接通过self.driver调用driver实例对象

填充实现-构造PO模型类图

@startuml

class BasePage{
    _url = null
    finds()
    find()
}

class MainPage{
    str element1
    goto_contact()
    goto_add_member()
}
class ContactPage{
    str element1
    ~ goto_add_member()
    ~ get_list()
}

class AddMemberPage{
    str element1
    ~ add_member()
    ~ add_member_fail()
}

BasePage<|-- MainPage: 继承
BasePage<|-- ContactPage: 继承
BasePage<|-- AddMemberPage: 继承

@enduml

优化用例-封装样板代码

  • 问题:
    • 直接调用selenium API,导致存在大量的样板代码,find、finds 等。
  • 解决方案:
    • 常用的UI操作封装在base_page中。

优化用例-提取页面元素

  • 问题:
    • 页面定位写在每个Page的方法中,如果此页面定位存在多处复用,那么需要多处修改
  • 解决方案:
    • 将页面定位抽离为私有类变量,符合六大原则中不要暴露页面内部的元素给外部

优化用例-添加起始页面的url

  • 问题:

    • 起始url 不应该写在basepage中,basepage和具体业务没有任何关联。
    • 每个UI用例的开始不可能都从首页开始,有的可能是从其他页面开始。
  • 解决方案:每个Page子类添加_base_url变量。确保即使用例从子类页面起始,也可以正常使用。