前言
网盗概念^-^相同的测试脚本使用不同的测试数据来执行,测试数据和测试行为完全分离, 这样的测试脚本设计模式称为数据驱动。(网盗结束)当我们测试某个网站的登录功能时,我们往往会使用不同的用户名和密码来验证登录模块对系统的影响,那么如果我们每一条数据都编写一条测试用例,这无疑是增加了代码量,代码重复,且显得那么臃肿(谁不喜欢身材好的呢?你懂的),这时候我们可以使用不同数据驱动代码执行相同的用例测试不同的场景。
实施数据驱动步骤
我们再来说说实施数据驱动测试的步骤:
1.创建/准备测试数据
2.封装读取数据的方法,保留测试脚本调用的接口/属性(我们需要传递给脚本什么参数)
3.编写自动化测试脚本
4.脚本中调用封装好的处理数据文件的模块并引入测试数据
5.执行测试脚本并分析测试结果
数据驱动测试环境准备
1.安装python3.x开发环境(能看到此文章的应该都有这个环境,没有的自行百度吧)
2.安装数据驱动模块ddt
安装方式1:cmd下执行命令 pip install ddt
安装方式2:https://pypi.org/simple/ddt/ 下载 并解压任意目录,cmd 运行命令python setup.py install
3.验证安装 pycharm 新建python文件并输入 import ddt 运行无报错信息既表示安装成功或者cmd 命令依次输入python回车 import ddt回车 无保存信息表示安装成功
4.unittest框架和ddt进行数据驱动
测试步骤
1.访问地址:https://mail.sohu.com/fe/#/login
2.输入用户名和密码
3.点击登录按钮
4.判断是否登录成功
数据存储
数据存在当前脚本中
数据准备
我们要实现的是用户登录的操作,所以用户名和密码是必须有的,期望结果可以有也可以没有。数据类型看源代码!
实例代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from selenium import webdriver 2 from ddt import ddt, data, unpack 3 import unittest 4 import time 5 from selenium.common.exceptions import NoSuchWindowException 6 ''' 7 简单数据驱动测试 8 ''' 9 @ddt 10 class ddtTest(unittest.TestCase): 11 # 数据 可以是元祖, 列表, 字典(可迭代对象) 12 value = [['13691579846@sohu.com', 'xiaochao11520','https://mail.sohu.com/fe/#/homepage'], 13 ['13691579844@sohu.com', 'xiaochao11520','https://mail.sohu.com/fe/#/homepage']] 14 # value = [{'uname':'******@sohu.com', 'password':'xiaochao11520','expected':'https://mail.sohu.com/fe/#/homepage'}, 15 # {'uname':'******@sohu.com', 'password':'xiaochao11520','expected':'https://mail.sohu.com/fe/#/homepage'}] 16 def setUp(self): 17 self.testUrl = 'https://mail.sohu.com/fe/#/login' 18 self.driver = webdriver.Firefox() 19 self.driver.get(self.testUrl) 20 21 @data(*value) # * 解析数据 22 @unpack# 用来解包, 将每组数据的第一个数据传递给uname依次类推, 当数据为字典时,形参需和字段的key值相同 23 def test_case1(self, uname, password, expected): 24 try: 25 username = self.driver.find_element_by_xpath("//input[@placeholder='请输入您的邮箱']") 26 username.send_keys(uname) 27 time.sleep(1) 28 userpassword = self.driver.find_element_by_xpath("//input[@placeholder='请输入您的密码']") 29 userpassword.send_keys(password) 30 self.driver.find_element_by_xpath("//input[@type='submit']").click() 31 time.sleep(2) 32 currenturl = self.driver.current_url 33 self.assertEqual(expected, currenturl,'登录失败') 34 except NoSuchWindowException as e: 35 print(e) 36 raise 37 except AssertionError: 38 print('期望值是{}, 实际值是{}'.format(expected,currenturl)) 39 raise 40 except Exception: 41 raise 42 def tearDown(self): 43 self.driver.quit() 44 # pass 45 if __name__ == '__main__': 46 unittest.main()
源码分析
1.@ddt来装饰测试类(ddt数据驱动的规范写法,记住就ok)
2.@data(*value)装饰测试用例(也是一种规范,这边又涉及到装饰器,不懂的可以百度或者看我之前的文章又介绍,这边不再赘述,一句话两句话也说不清楚)记住:*value作用是打散数据,比如上面代码是用一个大列表存储两个小列表存放数据的,那么*value会得到两个小列表,每个小列表是一组测试数据
3.@unpack 解析*value数据,会把两个小列表里面的每一个数据取出来分别传递给我们测试用例的形参
方式1缺点
存储大量数据时,需查看源代码,不利于脚本的维护
json文件读取测试数据进行数据驱动测试
数据准备
新建一个json文件(也可以是txt文件),将我们需要的两组测试数据以列表的形式写到json文件中,每组数据的每一项参数用相同的符号分割开(方便脚本读取数据)
实例代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{ "value1":"******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage", "value2":"******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage" }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from selenium import webdriver 2 from ddt import ddt, file_data 3 import unittest, time 4 from selenium.common.exceptions import NoSuchWindowException 5 import HTMLTestRunner 6 ''' 7 从文件中读测试数据 8 ''' 9 10 @ddt # ddt装饰测试类 11 class Testdata(unittest.TestCase): 12 13 def setUp(self): 14 self.driver = webdriver.Firefox() 15 self.driver.get('https://mail.sohu.com/fe/#/login') 16 17 @file_data('test_data.json') # 读取文件的 文件中数据可以是一个列表,也可以是一个字典 18 def test_data(self,value): 19 uname, password, expected = tuple(value.strip().split('||')) # value是一个字符串 20 # print(type(value),value) 21 try: 22 username = self.driver.find_element_by_xpath("//input[@placeholder='请输入您的邮箱']") 23 username.send_keys(uname) 24 time.sleep(1) 25 userpassword = self.driver.find_element_by_xpath("//input[@placeholder='请输入您的密码']") 26 userpassword.send_keys(password) 27 self.driver.find_element_by_xpath("//input[@type='submit']").click() 28 time.sleep(2) 29 currenturl = self.driver.current_url 30 self.assertEqual(expected, currenturl,'登录失败') 31 except NoSuchWindowException as e: 32 raise e 33 except AssertionError: 34 print('期望值是{}, 实际值是{}'.format(expected,currenturl)) 35 raise 36 except Exception: 37 raise 38 39 def tearDown(self): 40 self.driver.quit() 41 if __name__ == '__main__': 42 unittest.main() 43 # import os 44 # from datetime import date 45 # currentPath = os.path.dirname(os.path.abspath(__file__))# 获取当前文件目录 46 # reportPath = os.path.join(currentPath,'report') # 创建一个report目录 47 # if not os.path.exists(reportPath): 48 # os.mkdir(reportPath) # 判断目录是否存在, 不存在就创建 49 # reportName = os.path.join(reportPath, str(date.today())+'report.html') # 拼接html报告 50 # with open(reportName,'wb') as f: 51 # suite = unittest.TestLoader().loadTestsFromTestCase(Testdata) 52 # runner = HTMLTestRunner.HTMLTestRunner(stream=f,verbosity=1, title='数据驱动测试报告', description='数据驱动') 53 # runner.run(suite)
源码分析
1.相对上个实例,这里使用了@file_data(文件路径), 参数必须是一个文件,这里是一个json文件, 数据可以是一个列表,也可以是一个字典
# 列表形式
[
"*******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage",
"*******@sohu.com||xiaochao11520||https://mail.sohu.com/fe/#/homepage" ]
2.测试用例接收的是一个字符串,需要对字符串进行处理,把用户名,密码,期望值解析出来
方式2优缺点
测试数据存在文件中,方便管理修改,添加数据,易于维护,缺点呢?emmm个人认为这种方式最好!
从xml读取数据进行数据驱动测试
数据准备
新建一个xml格式的文件,按照xml格式的语法需求,填写数据(xml文档我也不是很懂,简单的显示个文字啥的还可以^-^!)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<?xml version="1.0"?> <bookList type="technolog"> <book> <uname>******@sohu.com</uname> <password>xiaochao11520</password> <expected>https://mail.sohu.com/fe/#/homepage</expected> </book> <book> <uname>******@sohu.com</uname> <password>xiaochao11520</password> <expected>https://mail.sohu.com/fe/#/homepage</expected> </book> </bookList>
实例代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from xml.etree import ElementTree 2 3 class ParseXml(object): 4 def __init__(self, xmlpath): 5 self.xmlpath = xmlpath 6 7 # 获取根节点 8 def getRoot(self): 9 tree = ElementTree.parse(self.xmlpath) 10 root = tree.getroot() 11 return root 12 13 # 根据根节点查找子节点 14 def findNodeByName(self, parentNode, nodeName): 15 nodes = parentNode.findall(nodeName) 16 return nodes 17 18 def getNodeOfChildText(self, node): 19 # 获取节点node下所有子节点的节点名作为key 20 # 本节点作为value组成的字典对象 21 childrenTextDict = {} 22 for i in list(node.iter())[1:]: # node 节点下的所有节点组成的列表 23 childrenTextDict[i.tag] = i.text 24 # print(list(node.iter())[1:]) 25 return childrenTextDict 26 27 # 获取节点node下面的节点的所有数据 28 def getDataFromXml(self, node): 29 root = self.getRoot() 30 books = self.findNodeByName(root, node) 31 dataList=[] 32 for book in books: 33 childrentext = self.getNodeOfChildText(book) 34 dataList.append(childrentext) 35 return dataList 36 if __name__=='__main__': 37 xml = ParseXml('./xmlData.xml') 38 root = xml.getRoot() 39 print(root.tag) 40 books = xml.findNodeByName(root, 'book') # 查找所有的book节点 41 for book in books: 42 # print(book[0].tag, book[0].text) 43 print(xml.getNodeOfChildText(book)) 44 print(xml.getDataFromXml('book'))
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from dataDdt.doXML import ParseXml 2 from selenium import webdriver 3 from selenium.common.exceptions import NoSuchWindowException, TimeoutException 4 import unittest 5 from ddt import ddt, data,unpack 6 import time 7 from selenium.webdriver.support import expected_conditions as EC 8 from selenium.webdriver.support.ui import WebDriverWait 9 from selenium.webdriver.common.by import By 10 values = ParseXml('./xmlData.xml') 11 @ddt 12 class xmltest(unittest.TestCase): 13 14 def setUp(self): 15 self.driver = webdriver.Firefox() 16 self.driver.get('https://mail.sohu.com/fe/#/login') 17 @data(*values.getDataFromXml('book')) 18 @unpack 19 def test_xml(self,uname, password, expected): 20 try: 21 wait = WebDriverWait(self.driver,5) 22 wait.until(EC.element_to_be_clickable((By.XPATH, "//input[@type='submit']"))) 23 username = self.driver.find_element_by_xpath("//input[@placeholder='请输入您的邮箱']") 24 username.send_keys(uname) 25 time.sleep(1) 26 userpassword = self.driver.find_element_by_xpath("//input[@placeholder='请输入您的密码']") 27 userpassword.send_keys(password) 28 self.driver.find_element_by_xpath("//input[@type='submit']").click() 29 time.sleep(2) 30 currenturl = self.driver.current_url 31 self.assertEqual(expected, currenturl, '登录失败') 32 except TimeoutException as e: 33 raise e 34 except NoSuchWindowException as e: 35 raise e 36 except AssertionError as e: 37 print('期望值是{}, 实际值是{}'.format(expected, currenturl)) 38 raise e 39 except Exception: 40 raise 41 def tearDown(self): 42 self.driver.quit() 43 44 if __name__=='__main__': 45 unittest.main()
源码分析
1.xml文档编写(深入了解需百度)有点像html,但又有不同,xml中的节点可以是任意名称,每个节点同样是成双出现
2.增加了doXML.py文档,用来解析xml文件,方便脚本获取数据(注释写的很详细,不懂的化可以慢慢调试,哪里不懂print哪里)
3.测试脚本和上面的实例大致相同(不懂的加我qq直接问)
方式3优缺点
优点是做到了数据与测试的分离,方便数据维护,缺点也比较明显,需要对xml文档有一定的了解
总结
上面的数据驱动测试步骤是我自己总结的,看了上面的实例对于步骤应该还算合理,下面是我在网上找到的数据驱动测试步骤(感觉比较官方!大家可以参考)
1.编写测试脚本, 脚本需要支持从程序对象, 文件,或者数据库读入数据。(个人观点:如果脚本先编写完,测试数据还未准备,后期还要做修改)
2.将测试脚本使用的测试数据存入程序对象,文件,或者数据库等外部介质中。(个人观点:这个阶段实为准备数据的阶段,也就是我们数据要存在哪里,理应放在第一步)
3.运行脚本过程中,循环调用存储在外部介质中的测试数据。(个人观点:这里要考虑我们如何读取,使用数据)
4.验证所有的测试结果是否符合预期结果
ps:源码已经上传到我的git,地址:https://github.com/13691579846/WebdriverAPI 欢迎交流讨论QQ交流群:878565760