自动化测试模型
基本概念
- 库
库的英文单词是Library,库是由代码集合成的一个产品,可供程序员调用。面向对象的代码组织形成的库叫类库,面向过程的代码组织形成的库叫函数库。从这个角度看,WebDriver就属于库的范畴,因为它提供了一组操作Web页面的类与方法,所以可以称它为Web自动化测试库。 - 框架
框架的英文单词是Framework,框架是为解决一个或一类问题而开发的产品,用户一般只需使用框架提供的类或函数,即可实现全部功能。从这个角度看,unittest框架主要用于测试用例的组织和执行,以及测试结果的生成。因为它的主要任务就是帮助我们完成测试工作,所以通常称它为测试框架。 - 工具
工具的英文单词是Tools,工具与框架所做的事情类似,只是工具提供了更高层次的封装,屏蔽了底层的代码,提供了单独的操作界面供用户使用。例如,UFT(QTP)、Katalon就属于自动化测试工具。
自动化测试模型
- 线性测试
通过录制或编写对应用程序的操作步骤会产生相应的线性脚本,每个线性脚本相对独立,且不产生依赖与调用。这是早期自动化测试的一种形式,即单纯地模拟用户完整的操作场景。 - 模块化与类库
线性测试的缺点是不易维护,因此早期的自动化测试专家开始思考用新的自动化测试模型来代替线性测试。做法很简单,借鉴了编程语言中的模块化思想,把重复的操作单独封装成公共模块。在测试用例执行过程中,当需要用到模块封装时对其进行调用,这样就最大限度地消除了重复,从而提高测试用例的可维护性。 - 数据驱动测试
虽然模块化测试很好地解决了脚本的重复问题,但是,自动化测试脚本在开发过程中还是发现了诸多不便。例如,在测试不同用户登录时,虽然登录的步骤是一样的,但是登录用的数据是不同的。模块化测试并不能解决这类问题。于是,数据驱动测试的概念被提出。
数据驱动测试的定义:数据的改变驱动自动化测试的执行,最终引起测试结果的改变,简单理解就是把数据驱动所需要的测试数据参数化,我们可以用多种方式来存储和管理这些参数化的数据。 - 关键字驱动测试
关键字驱动测试又被称为表驱动测试或基于动作字测试。这类框架会把自动化操作封装为“关键字”,避免测试人员直接接触代码,多以“填表格”的形式降低脚本的编写难度。
Robot Framework是主流的关键字驱动测试框架之一,它自带的Robot Framework RIDE可以编写的自动化测试用例。
这几种测试模型并非后者淘汰前者的关系,在实际实施过程中,往往需要相互结合使用。
模块化与参数化
模块化与参数化一般需要配合使用,即在创建函数或类方法时为它们设置入参,从而使它们可以根据不同的参数执行相应的操作。
下面用一个简单的例子介绍它们的用法。创建一个邮箱测试脚本test_mail.py。
from time import sleep
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://www.126.com")
#登录
sleep(2)
driver.switch_to.frame('x-URS-iframe')
driver.find_element_by_name('email').clear()
driver.find_element_by_name('email').send_keys('user_name')
driver.find_element_by_name('password').clear()
driver.find_element_by_name('password').send_keys('password')
driver.find_element_by_id('dologin').click()
#登录之后的动作
sleep(5)
#退出
driver.find_element_by_link_text('退出').click()
driver.quit()
假设要实现一个关于邮件的自动化测试项目,那么可能每条测试用例都需要有登录动作和退出动作。大部分测试用例都是在登录之后进行的,例如,发邮件,查看、删除、搜索邮件等。
此时,需要创建一个新的module.py文件来存放登录动作和退出动作。
class Mail:
def __init__(self,driver):
self.driver = driver
def login(self):
'''登录'''
self.driver.switch_to.frame('x-URS-iframe')
self.driver.find_element_by_name('email').clear()
self.driver.find_element_by_name('email').send_keys('user_name')
self.driver.find_element_by_name('password').clear()
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()
首先创建一个Mail类,在__init__()初始化方法中接收driver驱动并赋值给self.driver。
在login()和logout()方法中分别使用self.driver实现邮箱的登录动作和退出动作。
接下来修改test_mail.py,测试调用Mail类中的login()和logout()方法。
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()方法参数化。修改module.py文件代码如下。
#......
def login(self,username,password):
'''登录'''
self.driver.switch_to.frame('x-URS-iframe')
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').clear()
self.driver.find_element_by_name('password').send_keys(password)
self.driver.find_element_by_id('dologin').click()
这样就进一步提高了login()方法的可复用性,它不再使用一个固定的账号登录,而是根据被调用者传来的用户名和密码执行登录动作。
在测试用例中,可以用不同的数据调用login()方法。
#调用Mail类
mail = Mail(driver)
#登录账号为空
mail.login('','')
#用户名为空
mail.login('','password')
#密码为空
mail.login('username','')
#用户名/密码错误
mail.login('error','error')
#管理员登录
mail.login('admin','admin123')
#......
今天太阳也东升,而后西沉,早晨盛开的花儿也将凋谢;今天的太阳也西沉,而后东升,阳光照射之处遍地花开,但却已非昨日之花。