在自动化测试过程中,经常面临测试数据的维护和管理等问题。面对同一个功能如登录,登录流程每次都是一样的,只是测试数据不一样,这种情况在写测试用例时,需要创建多组不同的测试数据来检查登录功能的正确性。一种比较方便、高效的办法就是引用数据驱动测试。
DDT(Data-Driven Tests)数据驱动测试可以理解为:因测试数据的改变而驱动自动化测试的执行,最终引起测试结果的改变。通过数据驱动测试方法,每一组输入数据都对应一组测试用例,可以验证多组数据场景,如图2.37所示。
图2.37 DDT测试数据界面
进行数据驱动测试前,需要安装DDT模块。打开cmd命令提示符界面,输入“pip install ddt”,可以直接在线安装DDT,示例如下:
C:\Users\23939>pip install ddt Collecting ddt Using cached https://files.pythonhosted.org/packages/e4/d8/4649ee669e41760d824 63057215d93efa899ed0472a05b256b9ff7ba53c2/ddt-1.2.0-py2.py3-none-any.whl Installing collected packages: ddt Successfully installed ddt-1.2.0
在进行数据驱动测试实战中,需要在测试类上使用@ddt.ddt装饰器,在测试用例上使用@ddt.data装饰器。@ddt.data装饰器可以把参数作为测试数据,参数可以是单个值、列表、元组或字典。对于列表和元组,需要使用@ddt.unpack装饰器把元组和列表解析成多个参数。
示例如下:
import ddt import unittest Data = [{'name':"keep learing"}, {'age':18}, {'address':"深圳地区"}] @ddt.ddt class TestModules(unittest.TestCase): def setUp(self): print('testcase beaning....') def tearDown(self): print('testcase ending.....') @ddt.data(*Data) def test_DataDriver(self,Data): print('DDT数据驱动实战演示:',Data) if __name__ == '__main__': unittest.main()
运行结果如下:
testcase beaning.... ddt数据驱动实战演示: {'name': 'keep learing'} testcase ending..... testcase beaning.... ddt数据驱动实战演示: {'age': 18} testcase ending..... testcase beaning.... ddt数据驱动实战演示: {'address': '深圳地区'} testcase ending..... Ran 3 tests in 0.005s OK
上述案例中,不使用@ddt.unpack说明是两组测试数据,将Data内的每组数据分别作为参数传入@ddt.data()方法中,从而实现数据驱动测试。
使用@ddt.unpack装饰器解析列表或元组为多组参数,示例如下:
import ddt import unittest Data = [['admin','admin','登录失败'], ['admin','admin123','登录成功'], ['','','登录失败']] @ddt.ddt class TestModules(unittest.TestCase): def setUp(self): print('testcase beaning....') def tearDown(self): print('testcase ending.....') @ddt.data(*Data) @ddt.unpack def test_DataDriver(self,user,passwd,text): print('ddt数据驱动实战演示:',user) print('ddt数据驱动实战演示:',passwd) print('ddt数据驱动实战演示:',text) if __name__ == '__main__': unittest.main()
运行结果如下:
testcase beaning.... ddt数据驱动实战演示: adminddt数据驱动实战演示: admin ddt数据驱动实战演示: 登录失败 testcase ending..... testcase beaning.... ddt数据驱动实战演示: admin ddt数据驱动实战演示: admin123 ddt数据驱动实战演示: 登录成功 testcase ending..... testcase beaning.... ddt数据驱动实战演示: ddt数据驱动实战演示: ddt数据驱动实战演示: 登录失败 testcase ending.....
Data列表内存储的3组测试数据分别是账号、密码和文本信息。使用@ddt.upack方法将Data列表内的数据进行分解,分解后user、passwd和text分别对应每组测试数据中的账号、密码和文本信息。
DDT在自动化测试中的应用示例如下:
import ddt,unittest from time import sleep from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait # 导入WebDriverWait类 from selenium.webdriver.support import expected_conditions as EC # 导入EC模块 def readData(): return [ ['','','请输入账号密码'], ['admin123@sohu.com','','请输入账号密码'], ['admin111@sohu.com','','请输入账号密码'], ['','a123456789','请输入账号密码'], ] @ddt.ddt class TestLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.testUrl = "https://mail.sohu.com/fe/#/login" def tearDown(self): self.driver.quit() def by_css(self,usernameloc): '''重写css定位''' return self.driver.find_element_by_css_selector(usernameloc) def getassertText(self): '''获取验证信息''' try: sleep(2) loctor = (By.CSS_SELECTOR,'.tipHolder.ng-binding') WebDriverWait(self.driver,5,0.5).until(EC.presence_of_element_located ((loctor))) return self.by_css('.tipHolder.ng-binding').text except Exception as message: print('元素定位报错!报错原因是:{}'.format(message)) @ddt.data(*readData()) @ddt.unpack def test_souhuLogin(self,user,passwd,text): self.driver.get(self.testUrl) sleep(3) self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user) self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd) self.by_css('.btn-login.fontFamily').click() self.assertEqual(self.getassertText(),text) if __name__ == '__main__': unittest.main()
readData()方法中定义了3组测试数据,分别是登录账号、密码和验证信息。测试用例上的@ddt.data把readData()方法中的参数作为测试数据,@ddt.unpack用于将readData()方法中的列表分解为多组测试数据。by_css()方法是对源生css定位的二次封装,getassertText()方法用来获取页面的文本信息以做断言使用。
在实际自动化测试任务中,很多情况下会将测试脚本中的测试数据和测试用例分开管理,一般会将测试数据单独存放在Excel中进行维护。Python提供了很多第三方的模块来读取Excel数据,如xlrd、xlwt和xlutils等。本小节主要讨论xlrd在自动化测试中的应用。
首先安装xlrd模块,打开cmd命令提示符界面,输入“pip install xlrd”进行在线安装,示例如下:
C:\Users\23939>pip install xlrd Collecting xlrd Using cached https://files.pythonhosted.org/packages/07/ec4e2xlrd-1.1.0-py2.py3- none-any.whl Installing collected packages: xlrd Successfully installed xlrd-1.1.0
出现上述结果表示安装成功。
示例如下:
import xlrd import ddt,unittest from time import sleep from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait # 导入WebDriverWait类 from selenium.webdriver.support import expected_conditions as EC # 导入EC模块 def readUserName(row): '''读取用户名''' book = xlrd.open_workbook('datainfo.xlsx','r') table = book.sheet_by_index(0) return table.row_values(row)[0] def readPasswd(row): '''读取用户名''' book = xlrd.open_workbook('datainfo.xlsx','r') table = book.sheet_by_index(0) return table.row_values(row)[1] def readAssertText(row): '''读取预期结果''' book = xlrd.open_workbook('datainfo.xlsx','r') table = book.sheet_by_index(0) return table.row_values(row)[2]
readUserName()、readPasswd()、AssertText()方法分别用于读取datainfo.xlsx表中每一行的用户名、密码和文本验证信息。
class TestSouHuLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.testUrl = "https://mail.sohu.com/fe/#/login" def tearDown(self): self.driver.quit() def by_css(self,usernameloc): '''重写css定位''' return self.driver.find_element_by_css_selector(usernameloc) def getassertText(self): '''获取验证信息''' try: sleep(2) loctor = (By.CSS_SELECTOR,'.tipHolder.ng-binding') WebDriverWait(self.driver,5,0.5).until(EC.presence_of_element_located ((loctor))) return self.by_css('.tipHolder.ng-binding').text except Exception as message: print('元素定位报错!报错原因是:{}'.format(message)) def souhuLogin(self,user,passwd): '''封装登录功能''' self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user) self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd) self.by_css('.btn-login.fontFamily').click() def test_souHuLogin_001(self): '''账号和密码为空:登录失败''' self.driver.get(self.testUrl) sleep(3) self.souhuLogin(readUserName(1),readPasswd(1)) self.assertEqual(self.getassertText(),readAssertText(1)) def test_souHuLogin_002(self): '''账号正确和密码为空:登录失败''' self.driver.get(self.testUrl) self.souhuLogin(readUserName(2),readPasswd(2)) self.assertEqual(self.getassertText(),readAssertText(2)) def test_souHuLogin_003(self): '''账号错误和密码为空:登录失败''' self.driver.get(self.testUrl) self.souhuLogin(readUserName(3),readPasswd(3)) self.assertEqual(self.getassertText(),readAssertText(3)) def test_souHuLogin_004(self): '''账号为空和密码正确:登录失败''' self.driver.get(self.testUrl) self.souhuLogin(readUserName(4),readPasswd(4)) self.assertEqual(self.getassertText(),readAssertText(4)) if __name__ == '__main__': unittest.main()
在TestSouHuLogin测试类中,对css定位并进行二次封装为by_css()方法,将搜狐邮箱登录功能封装为souhuLogin()方法,调用souhuLogin()方法时只需要输入参数user、passwd即可。此外,还分别定义了4组测试用例进行登录功能的验证。getassertText()方法用于获取页面实际验证信息,并与预期结果readAssertText()方法返回的文本值进行比对,如果信息一致则测试用例通过,反之,则测试用例失败。
首先在Excel表中写入以下数据,如图2.38所示。
图2.38 DDT+Excel测试数据界面
示例如下:
import xlrd import ddt,unittest from time import sleep from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait # 导入WebDriverWait类 from selenium.webdriver.support import expected_conditions as EC # 导入EC模块 def readData(): book = xlrd.open_workbook('datainfo.xlsx','r') # 读取datainfo.xlsx表 table = book.sheet_by_index(0) # 获取第一个sheet newRows = [] for rowValue in range(1,table.nrows): newRows.append(table.row_values(rowValue,0,table.ncols)) return newRows # 返回新的newRows
readData()方法用于读取datainfo.xlsx表中每一行的用户名、密码和文本信息,然后将读取结果分别依次追加到newRows。
@ddt.ddt class TestLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.testUrl = "https://mail.sohu.com/fe/#/login" def tearDown(self): self.driver.quit() def by_css(self,usernameloc): '''重写css定位''' return self.driver.find_element_by_css_selector(usernameloc) def getassertText(self): '''获取验证信息''' try: sleep(2) loctor = (By.CSS_SELECTOR,'.tipHolder.ng-binding') WebDriverWait(self.driver,5,0.5).until(EC.presence_of_element_located ((loctor))) return self.by_css('.tipHolder.ng-binding').text except Exception as message: print('元素定位报错!报错原因是:{}'.format(message)) @ddt.data(*readData()) @ddt.unpack def test_souhuLogin(self,user,passwd,text): self.driver.get(self.testUrl) sleep(3) self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user) self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd) self.by_css('.btn-login.fontFamily').click() self.assertEqual(self.getassertText(),text) if __name__ == '__main__': unittest.main()
引入ddt模块,在测试类上增加@ddt.ddt装饰器,在测试用例上增加@ddt.data、@ddt.unpack方法。其中@ddt.data方法用于把newRows参数作为测试数据,newRows参数实际是一个嵌套的列表;@ddt.unpack方法用于把测试数据分解为多个值并且作为实际参数传入测试用例中的user、passwd和text中。getassertText()方法用来获取页面实际文本信息,然后与text预期文本进行比较,验证信息一致则通过。
YAML是一种直观的能够被计算机识别的数据序列化格式,容易阅读,并且容易和脚本语言交互。YAML类似于XML,但是语法比XML简单得多;而对于JSON,YAML可以写成规范化的配置文件。此外,不管是做Web自动化测试还是接口自动化测试,都可以使用YAML来管理测试数据,这种方法也比较简单高效。
YAML的安装非常简单,可以直接在线使用pip命令进行安装。打开cmd命令提示符界面,输入“pip install pyyaml”进行在线安装,示例如下:
C:\Users\23939>pip install pyyaml Collecting pyyaml Downloading https://files.pythonhosted.org/packages/4f/ca/5fad249c5032270540c24d21 89b0ddf1396aac49b0bdc548162edcf14131/PyYAML-3.13-cp36-cp36m-win_amd64.whl (206kB) 100% | ████████████████████████████████ | 215kB 98kB/s Installing collected packages: pyyaml Successfully installed pyyaml-3.13
进入Python交互模式,来验证YAML是否安装成功,如下所示表示安装成功:
C:\Users\23939>python Python 3.6.4 (v3.6.4:d48eceb,Dec 19 2017,06:54:40) [MSC v.1900 64 bit (AMD64)] on win32 Type "help","copyright","credits" or "license" for more information. >>> import yaml >>>
YAML在自动化测试中使用data.yaml文件存储的数据信息如下,同时注意YAML文件的扩展名必须是以.yaml结尾。
userNull: username: "" password: "" assertText: "请输入账号密码" passNull: username1: "amdin888" password1: "" assertText1: "请输入账号密码"
示例如下:
import unittest,yaml from time import sleep from selenium import webdriver def readYaml(): '''获取所有YAML数据''' f = open('data.yaml','r',encoding='utf-8') data = yaml.load(f) f.close() return data
导入YAML模块,readYaml()方法下的open()方法用于打开data.yaml文件,为了防止乱码可使用encoding="utf-8"来声明。yaml.load()方法用于读取data.yaml文件下的所有数据。close()方法用于关闭整个yaml文件,如果不关闭会产生ResourceWarning提示,示例如下:
class TestLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.testUrl = "https://mail.sohu.com/fe/#/login" def tearDown(self): self.driver.quit() def by_css(self,usernameloc): '''重写css定位''' return self.driver.find_element_by_css_selector(usernameloc) def getassertText(self): '''获取验证信息''' try: return self.by_css('.tipHolder.ng-binding').text except Exception as message: print('元素定位报错!报错原因是:{}'.format(message)) def souhuLogin(self,user,passwd): '''封装登录功能''' self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user) self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd) self.by_css('.btn-login.fontFamily').click() def test_souHuLogin_001(self): '''账号正确和密码为空:登录失败''' self.driver.get(self.testUrl) sleep(3) self.souhuLogin(readYaml()['userNull']['username'],readYaml()['userNull'] ['password']) self.assertEqual(self.getassertText(),readYaml()['userNull']['assertText']) def test_souHuLogin_002(self): '''账号错误和密码为空:登录失败''' self.driver.get(self.testUrl) sleep(3) self.souhuLogin(readYaml()['passNull']['username1'],readYaml()['passNull'] ['password1']) self.assertEqual(self.getassertText(),readYaml()['passNull']['assertText1']) if __name__ == '__main__': unittest.main()
readYaml()方法返回所有的YAML数据,然后通过索引来获取指定的测试数据。例如,['username']、['password']和'assertText'分别访问userNull节点下的用户名信息、密码信息和预期文本信息。
parameterized翻译过来是参数化,是对unittest单元测试框架的参数化扩展。简单来说,它可以结合unittest单元测试框架进行一些参数化策略的应用。
1. 在线安装parameterized
parameterized的安装十分简单,可以直接在线安装。打开cmd命令提示符界面,输入“pip install parameterized”进行在线安装。如下所示表示安装成功:
C:\Users\23939>pip3 install parameterized Collecting parameterized Downloading https://files.pythonhosted.org/packages/65/d4/b0b626eb263a4c2aa3ca3cd2 0ea3db410db837f7f6b5d3fc4a6c4bee3631/parameterized-0.6.1-py2.py3-none-any.whl Installing collected packages: parameterized Successfully installed parameterized-0.6.1 C:\Users\23939>
2. parameterized在自动化测试中的应用
示例如下:
import unittest from selenium import webdriver from time import sleep from parameterized import parameterized # 导入参数化模块 class LoginTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() cls.testUrl = 'https://mail.sohu.com/fe/#/login' @classmethod def tearDownClass(cls): cls.driver.quit() def by_css(self,usernameloc): '''重写css定位''' return self.driver.find_element_by_css_selector(usernameloc) def getassertText(self): '''获取验证信息''' try: return self.by_css('.tipHolder.ng-binding').text except Exception as message: print('元素定位报错!报错原因是:{}'.format(message)) def souhuLogin(self,user,passwd): '''封装登录功能''' self.by_css('.addFocus.ipt-account.ng-pristine.ng-valid').send_keys(user) self.by_css('.addFocus.ng-pristine.ng-valid').send_keys(passwd) self.by_css('.btn-login.fontFamily').click()
上述代码对搜狐邮箱登录、css元素定位和登录后的验证信息功能进行二次封装,封装后的方法分别是souhuLogin()、by_css()和getassertText()。
@parameterized.expand([ ('','','请输入账号密码'), ('admin111@sohu.com','','请输入账号密码'), ('','a123456789','请输入账号密码')]) def test_login(self,username,password,assert_text): # 登录系统 self.driver.get(self.testUrl) sleep(3) self.souhuLogin(username,password) self.assertEqual(self.getassertText(),assert_text) if __name__ == '__main__': unittest.main(verbosity=2)
@parameterized.expand()方法用来存放多组测试数据,每组测试数据都会作为实参传递给测试用例中的username、password和assert_text。getassertText()方法返回的登录页面实际结果和assert_text定义好的预期结果进行比较,从而实现参数化登录过程。