5.自动化测试框架

1.基本概念

(1)库

库的英文单词是 Library,库是由代码集合成的一个产品,可供程序员调用。面向对象的代码组织形成的库叫类库,面向过程的代码组织形成的库叫函数库。
WebDriver 就属于库的范畴,因为它提供了一组操作 Web 页面的类与方法,所以可以称它为 Web 自动化测试库。

(2)框架

框架的英文单词是 Framework,框架是为解决一个或一类问题而开发的产品,用户一般只需使用框架提供的类或函数,即可实现全部功能。
unittest 框架主要用于测试用例的组织和执行,以及测试结果的生成。因为它的主要任务就是帮助我们完成测试工作,所以通常称它为测试框架。

(3)工具

工具的英文单词是 Tools,工具与框架所做的事情类似,只是工具提供了更高层次的封装,屏蔽了底层的代码,提供了单独的操作界面供用户使用。例如,UFT(QTP)、Katalon就属于自动化测试工具。

2.自动化测试模型

(1)线性测试

通过录制或编写对应用程序的操作步骤会产生相应的线性脚本,每个线性脚本相对独立,且不产生依赖与调用。这是早期自动化测试的一种形式,即单纯地模拟用户完整的操作场景。第4篇文章内容的自动化测试例子就属于线性测试。

(2)模块化与类库

线性测试的缺点是不易维护,故出现了新的自动化测试模型来代替线性测试。
该模型借鉴了编程语言中的模块化思想,把重复的操作单独封装成公共模块。在测试用例执行过程中,当需要用到模块封装时对其进行调用,这样就最大限度地消除了重复,从而提高测试用例的可维护性。

(3)数据驱动测试

模块化测试也有不足之处,例如在测试不同用户登录时,虽然登录的步骤是一样的,但是登录用的数据是不同的。模块化测试并不能解决这类问题。于是,数据驱动测试的概念被提出。
数据驱动测试的定义:数据的改变驱动自动化测试的执行,最终引起测试结果的改变。简单理解就是把数据驱动所需要的测试数据参数化,我们可以用多种方式来存储和管理这 些参数化的数据。

(4)关键字驱动测试

关键字驱动测试又被称为表驱动测试或基于动作字测试。这类框架会把自动化操作封装为“关键字”,避免测试人员直接接触代码,多以“填表格”的形式降低脚本的编写难度。
Robot Framework 是主流的关键字驱动测试框架之一,通过它自带的 Robot Framework RIDE 编写自动化测试用例。

这几种测试模型并非后者淘汰前者的关系,在实际实施过程中,往往需要相互结合使用。

3.模块化与参数化

模块化与参数化一般需要配合使用,即在创建函数或类方法时为它们设置入参,从而使它们可以根据不同的参数执行相应的操作。
创建一个邮箱测试脚本 test_mail.py

from time import sleep
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("http://www.126.com")

# 登录
sleep(2)
frame = driver.find_element_by_css_selector("iframe[id^='x-URS-iframe']")
driver.switch_to.frame(frame) # 默认直接传入id或name属性值
driver.find_element_by_name('email').clear()
driver.find_element_by_name('email').send_keys('username')
driver.find_element_by_name('password').click()
driver.find_element_by_name('password').send_keys('password')
driver.find_element_by_id('dologin').click()

# 登录之后的动作
driver.find_element_by_link_text('退出').click()

driver.switch_to.default_content()
driver.quit()

假设要实现一个关于邮箱的自动化测试项目,那么可能每条测试用例都需要有登录动作和退出动作。。此时,需要创建一个新的 module.py 文件来存放登录动作和退出动作。代码如下:

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

    def login(self):
        self.driver.switch_to.frame()
        frame = self.driver.find_element_by_css_selector("iframe[id^='x-URS-iframe']")
        self.driver.switch_to.frame(frame)  # 默认直接传入id或name属性值
        self.driver.find_element_by_name('email').clear()
        self.driver.find_element_by_name('email').send_keys('username')
        self.driver.find_element_by_name('password').click()
        self.driver.find_element_by_name('password').send_keys('password')
        self.driver.find_element_by_id('dologin').click()

    def logout(self):
        self.driver.find_element_by_link_text('退出').click()

修改 test_mail.py,调用 Mail 类中的方法:

from time import sleep
from selenium import webdriver
from module import Mail

driver = webdriver.Chrome()
driver.get("http://www.126.com")

# 调用Mail类并接受driver驱动
mail = Mail(driver)
# 登录
mail.login()
# 登录之后的动作
sleep(5)
# 退出
mail.logout()

driver.quit()

在编写测试用例过程中,如果需要用到登录动作和退出动作,那么只需调用 Mail 类中的 login()方法和 logout()方法即可,这将大大提高测试代码的可复用性。
当我们需要测试登录功能,虽然登录步骤是固定的,但是测试的数据(账号)不同,这时就需要把 login()方法参数化,进一步提高 login() 方法的复用性。修改 module.py 部分代码如下:

    def login(self, username, password):
        self.driver.switch_to.frame()
        frame = self.driver.find_element_by_css_selector("iframe[id^='x-URS-iframe']")
        self.driver.switch_to.frame(frame)  # 默认直接传入id或name属性值
        self.driver.find_element_by_name('email').clear()
        self.driver.find_element_by_name('email').send_keys(username)
        self.driver.find_element_by_name('password').click()
        self.driver.find_element_by_name('password').send_keys(password)
        self.driver.find_element_by_id('dologin').click()

在测试用例中,可以用不同的数据调用 login() 方法:

# 调用Mail类
mail = Mail(driver)
# 登录账号为空
mail.login("", "")
# 用户名为空
mail.login("", "password")
# 密码为空
mail.login("username", "")
# 用户名/密码错误
mail.login("error", "error")
# 管理员登录
mail.login("admin", "admin123")
#.....

4.读取数据文件

前面例子用到了参数化,但大多数测试更倾向于把测试数据放到数据文件中。介绍几种常见的读取数据文件的方法。

(1)读取 txt 文件

Python 提供了以下几种读取 txt 文件的方法。

  • read():读取整个文件。
  • readline():读取一行数据。
  • readlines():读取所有行的数据。

创建 ./data_file/user_info.txt 文件如下:(用户名和密码按行写入 txt 文件中,用户名和密码之间用冒号“:”隔开。)

:123
user:
error:error
admin:admin123

创建 read_txt.py 文件,用于读取 txt 文件:

with(open("./data_file/user_info.txt", "r")) as user_file:
    data = user_file.readlines()

# 格式化处理
users = []
for line in data:
    user = line[:-1].split(':')
    users.append(user)

# 打印 users 二维数组
print(users)

在这里插入图片描述
[:-1] 可对字符串进行切片,以省略最后一个字符,因为读取的每一行数据结尾都有一个换行符“\n”。
split()通过冒号(:)对每行数据进行拆分,会得到数组[’’, ‘123’] 。

取 users 数组中的数据,将得到的数组用不同的用户名/密码进行登录:

# 调用Mail类并接受driver驱动
mail = Mail(driver)
# 用户名为空
mail.login(users[0][0], users[0][1])
# 密码为空
mail.login(users[1][0], users[1][1])
# 用户名/密码错误
mail.login(users[2][0], users[2][1])
# 管理员登录
mail.login(users[3][0], users[3][1])
#....

(2)读取 CSV 文件

CSV 文件可用来存放固定字段的数据。
创建 user_info.csv 文件如下:
5.自动化测试框架_第1张图片
(PS:不要直接修改文件的后缀名来创建 CSV 文件,因为这样的文件并非真正的CSV 类型的文件。)
创建 read_csv.py 文件对 CSV 文件进行读取:

import csv
import codecs
from itertools import islice

# 读取本地 CSV 文件
data = csv.reader(codecs.open("./data_file/user_info.csv", "r", "gb2312"))

# 存放用户数据
users = []
# 循环输出每行信息
for line in islice(data, 1, None):
    users.append(line)

print(users)

在这里插入图片描述
通过 Python 读取 CSV 文件比较简单,但会遇到两个问题。

  • 中文乱码问题。数据文件中我们不可避免会使用中文,codecs 是 Python 标准的模块编码和解码器。通过 codecs 提供的 open()方法,在打开文件时可以指定编码类型,如 gb2312;然后,导入 csv 模块,通过 reader()方法读取文件,即避免中文乱码问题。
  • 跳过 CSV 文件的第一行。我们一般会在第一行定义测试字段名,所以在读取数据时需要跳过。Python 的内建模块 itertools 提供了用于操作迭代对象的函数,即 islice()函数,它可以返回一个迭代器,第一个参数指定迭代对象,第二个参数指定开始迭代的位置,第三个参数表示结束位。

(3)读取 XML 文件

有时我们需要读取的数据是不规则的。例如,我们需要用一个配置文件来配置当前自动化测试平台、浏览器、URL、登录的用户名和密码等,这时就可以使用 XML 文件来存放这些测试数据。
创建 ./data_file/config.xml 文件,代码如下:


<info>
    <platforms>
        <platform>Windowsplatform>
        <platform>Linuxplatform>
        <platform>macOSplatform>
    platforms>
    <browsers>
        <browser>Firefoxbrowser>
        <browser>Chromebrowser>
        <browser>Edgebrowser>
    browsers>
    <url>http://www.xxxx.comurl>
        <login username="admin" password="123456">login>
        <login username="guest" password="654321">login>
info>

通过代码可以看出,数据主要存放在标签对之间,如Windows。或者是作为标签的属性存放,如
以上述 config.xml 文件为例,介绍读取 XML 文件的方法。
a. 获得标签对之间的数据

from xml.dom.minidom import parse

# 打开 XML 文件
dom = parse('./data_file/config.xml')
# 得到文档元素对象
root = dom.documentElement
# 获得(一组)标签
tag_name = root.getElementsByTagName('platform')

print(tag_name[0].firstChild.data)
print(tag_name[1].firstChild.data)
print(tag_name[2].firstChild.data)

5.自动化测试框架_第2张图片
Python 自 带 读 取 XML 文件的模块,通过 parse() 方 法 可 读 取 XML 文件。documentElement() 方法用于获取文档元素对象,getElementsByTagName() 方法用于获取文件中的标签。
我们不需要指定标签的层级关系,即获取的标签可以是任意层级的,之所以在定义 XML 文件时设置层级,仅仅是为了方便阅读。
firstChild 属性可返回被选节点的第一个子节点,data 表示获取该节点的数据,它和 WebDriver 中的 text 语句作用相似。

b. 获取标签的属性值

from xml.dom.minidom import parse

dom = parse('./data_file/config.xml')
root = dom.documentElement

login_info = root.getElementsByTagName('login')

# 获得第一个 login 标签 的 username 和 password 属性值
username = login_info[0].getAttribute("username")
password = login_info[0].getAttribute("password")
print(username, password)
# 获得第二个 login 标签 的 username 和 password 属性值
username = login_info[1].getAttribute("username")
password = login_info[1].getAttribute("password")
print(username, password)

5.自动化测试框架_第3张图片
使用 getAttribute()方法获取元素的属性值,它和 WebDriver 中的 get_attribute()方法作用相似。

(4)读取 JSON 文件

JSON 是一种轻量级的数据交换格式,清晰的层次结构使得 JSON 文件被广泛使用。
创建 user_info.json 文件,如下:(json文件中都使用双引号)

[
  {"username": "", "password": ""},
  {"username": "", "password": "123"},
  {"username": "user", "password": ""},
  {"username": "error", "password": "error"},
  {"username": "admin", "password": "admin123"}
]

创建 read_json.py 文件,代码如下:

import json

with open('./data_file/user_info.json', 'r') as f:
    data = f.read()

user_list = json.loads(data)
print(user_list)

5.自动化测试框架_第4张图片
因为测试数据本身是以列表和字典格式存放的,所以读取整个文件内容后,通过 JSON 提供的表将 str 类型转换为 list 类型即可。

在自动化测试中,数据驱动必须和单元测试框架一起讨论才有意义,学习完 unittest 单元测试框架之后再来学习数据驱动。

你可能感兴趣的:(自动化,测试工具,运维)