数据驱动测试--基于Python

在自动化测试过程中,经常面临测试数据的维护和管理等问题。如对登录进行测试,每次流程都是一样,只是测试数据不一样,这种情况在写测试用例时,需要创建多组不同的测试数据来检查登录功能的正确性。这时,就可以使用数据驱动测试。

DDT

DDT(Data-Driven Tests)数据驱动测试:因测试数据的改变而驱动自动化测试的执行,最终引起测试结果的改变。通过数据驱动测试的方法,每一组输入数据都对应一组测试用例,可以验证多组数据场景

安装DDT

Github地址:DDT

pip install ddt

DDT在自动化测试中的应用

在进行数据驱动测试实战中,需要在测试类上使用@ddt.ddt装饰器,在测试用例上使用@ddt.data装饰器。@ddt.data装饰器可以把参数作为测试数据,参数可以是单个值,列表、元组或字典。对于列表和元组,需要使用@ddt.unpack装饰器吧元组合列表解析成多个参数。

示例

import ddt
import unittest


data = [
    {"name": "ddt"},
    {"age": 5},
    {"address": "know"}
]


@ddt.ddt
class TestSample(unittest.TestCase):

    def setUp(self):
        print("Test Case Begining........")

    @ddt.data(*data)
    def test_datadrivendata(self, data):
        print("数据驱动测试:", data)

    def tearDown(self):
        print("Test Case Ending...................")


if __name__ == "__main__":
    unittest.main()

执行结果:

Testing started at 下午 17:48 ...
D:\ENV_DIRECTOR\Envs\py3_heima\Scripts\python.exe "F:\Pycharm\PyCharm 2017.3.3\helpers\pycharm\_jb_unittest_runner.py" --path D:/zhenghou/python_learning/test_ddt/test_ddt1.py
Launching unittests with arguments python -m unittest D:/zhenghou/python_learning/test_ddt/test_ddt1.py in D:\zhenghou\python_learning\test_ddt
Test Case Begining........
数据驱动测试: {'name': 'ddt'}
Test Case Ending...................
Test Case Begining........
数据驱动测试: {'age': 5}
Test Case Ending...................
Test Case Begining........
数据驱动测试: {'address': 'know'}
Test Case Ending...................


Ran 3 tests in 0.002s

OK

不使用@ddt.unpack说明是两组测试数据,将data内的每组数据跟别作为参数传入@ddt.data()方法中,从而实现数据驱动测试。
使用@ddt.unpack装饰器解析列表或元组为多组参数。

import ddt
import unittest


data = [
    ["ddt", 5],
    ['robotframework', 4],
    ['unittest', 10]
]


@ddt.ddt
class TestSample(unittest.TestCase):

    def setUp(self):
        print("Test Case Begining........")

    @ddt.data(*data)
    @ddt.unpack
    def test_datadrivendata(self, username, value):
        print("数据驱动测试--username:", username)
        print("数据驱动测试--value:", value)

    def tearDown(self):
        print("Test Case Ending...................")


if __name__ == "__main__":
    unittest.main()

执行结果

Testing started at 下午 17:55 ...
D:\ENV_DIRECTOR\Envs\py3_heima\Scripts\python.exe "F:\Pycharm\PyCharm 2017.3.3\helpers\pycharm\_jb_unittest_runner.py" --path D:/zhenghou/python_learning/test_ddt/test_ddt1.py
Launching unittests with arguments python -m unittest D:/zhenghou/python_learning/test_ddt/test_ddt1.py in D:\zhenghou\python_learning\test_ddt
Test Case Begining........
数据驱动测试--username: ddt
数据驱动测试--value: 5
Test Case Ending...................
Test Case Begining........
数据驱动测试--username: robotframework
数据驱动测试--value: 4
Test Case Ending...................
Test Case Begining........
数据驱动测试--username: unittest
数据驱动测试--value: 10
Test Case Ending...................


Ran 3 tests in 0.005s

OK

Process finished with exit code 0

以百度搜索为例,验证输入后校验title,在readData()方法中定义3组数据,分别是执行那个case,输入内容,和失败后提示信息。@unpack用于将readData()方法中的列表等分解为多组测试数据。

import time

import unittest
import ddt
from selenium import webdriver


def readData():
    # 列表
    # data = [
    #     ["case1", "selenium", '校验失败'],
    #     ["case2", "ddt", "not true"],
    #     ['case3', "unittest", "hahah, you False"]
    # ]
    # 元组
    # data = (
    #     ("case1", "selenium", '校验失败'),
    #     ("case2", "ddt", "not true"),
    #     ('case3', "unittest", "hahah, you False")
    # )
    # 字典
    data = (
        {"case": "case1", "search_key": "selenium", "text": "校验失败"},
        {"case": "case2", "search_key": "ddt", "text": "not true"},
        {"case": "case3", "search_key": "unittest", "text": "hahah, you False"},

    )
    return data


@ddt.ddt
class TestBadiduSearch(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()

    def baidu_search(self, search_key):
        self.driver.get("http://www.baidu.com")
        self.driver.find_element_by_id("kw").send_keys(search_key)
        self.driver.find_element_by_id("su").click()
        time.sleep(3)

    @ddt.data(*readData())
    @ddt.unpack
    def test_search1(self, case, search_key, text):
        print("正在执行" + case + "==============================")
        self.baidu_search(search_key)
        self.assertEqual(self.driver.title, search_key + "_百度搜索", text)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == '__main__':
    unittest.main()

此外DDT还支持数据文件的参数化。它封装了数据文件的读取,让测试人员更专注于数据文件中的内容,以及在测试用例中的使用,而不需要关心数据文件是如何读取进来的。
首先,创建ddt_data_file.json文件

{
	"case1": {"search_key": "selenium", "case": "case1"},
	"case2": {"search_key": "ddt", "case": "case2"},
	"case3": {"search_key": "unittest", "case": "case3"}
}

使用json文件中的数据

    @ddt.file_data("ddt_data_file.json")
    def test_datadrivendata(self,search_key, case):
        print("数据驱动测试------", search_key)
        print("数据驱动测试------test", case)

DDT还支持yaml格式的数据文件,

Excel

在实际自动化测试任务中,很多情况下会将测试脚本中的测试数据和测试用例分开管理,一般会将测试数据单独存放在Excel文件中。
python操作excel主要用到xlrd和xlwt这两个库,即xlrd是读excel,xlwt是写excel的库。

安装

pip install xlrd

xlrd使用

打开excel文件并创建对象存储

import xlrd

book = xlrd.open_workbook('test.xlsx')

获取一个工作表

  1. 打印所有的sheet名
book = xlrd.open_workbook('test.xlsx')
print(book.sheet_names())
  1. 通过索引顺序获取
# 第一种通过索引获取
sheet1 = book.sheets()[0]

# 第二种通过索引获取
sheet1 = book.sheet_by_index(0)
  1. 通过sheet名获取
sheet1 = book.sheet_by_name("Sheet1")

获取整行和整列的值(数组)

# 行
sheet1.row_values(0)

# 列
sheet1.col_values(i)

获取行数和列数

# 行数
sheet.nrows

# 列数
sheet.ncols

循环行列表的数据

nrows = sheet1.nrows

for i in range(nrows):
    print(sheet1.row_values(i))

单元格

单元格的操作 ·

# table.cell(rowx,colx)   #返回单元格对象
sheet1.cell(1, 0).value

# table.cell_type(rowx,colx)    #返回单元格中的数据类型
# 类型 0 empty,1 string, 2 number, 3 date, 4 boolean, 5 error
sheet.cell_type(1, 1)

table.cell_value(rowx,colx)   #返回单元格中的数据

table.cell_xf_index(rowx, colx)   # 暂时还没有搞懂

Excel 整合 DDT

import time

import ddt
import xlrd
import unittest
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC


def readData():
    book = xlrd.open_workbook("test.xlsx")
    sheet = book.sheet_by_index(0)  # 获取第一个sheet表
    newRows = []
    for row in range(1, sheet.nrows):
        newRows.append(sheet.row_values(row, 0, sheet.ncols))
    return newRows


@ddt.ddt
class TestBaiduSearch(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()

    def baiduSearch(self, search_text):
        self.driver.get("http://www.baidu.com")
        self.driver.find_element_by_id("kw").send_keys(search_text)
        self.driver.find_element_by_id("su").click()
        time.sleep(3)

    @ddt.data(*readData())
    @ddt.unpack
    def test_case(self, search_text, assert_text):
        self.baiduSearch(search_text)
        self.assertEqual(self.driver.title, assert_text)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == "__main__":
    unittest.main()

csv

CSV (Comma Separated Vaules) 格式是电子表格和数据库中最常见的输入、输出文件格式。
csv 模块实现了 CSV 格式表单数据的读写。其提供了诸如“以兼容 Excel 的方式输出数据文件”或“读取 Excel 程序输出的数据文件”的功能,程序员无需知道 Excel 所采用 CSV 格式的细节。此模块同样可以用于定义其他应用程序可用的 CSV 格式或定义特定需求的 CSV 格式。
csv 模块中的 reader 类和 writer 类可用于读写序列化的数据。也可使用 DictReader 类和 DictWriter 类以字典的形式读写数据。
建立csv文件如下:

username,password,assert_text
admin1,psd1,ggg
admin2,psd2,hhh
admin3,psd3,jjj

Reader()

class csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds)

创建一个对象,该对象在操作上类似常规 writer,但会将字典映射到输出行。 fieldnames 参数是由键组成的 序列,它指定字典中值的顺序,这些值会按指定顺序传递给 writerow() 方法并写入文件 f。 如果字典缺少 fieldnames 中的键,则可选参数 restval 用于指定要写入的值。 如果传递给 writerow() 方法的字典的某些键在 fieldnames 中找不到,则可选参数 extrasaction 用于指定要执行的操作。 如果将其设置为默认值 ‘raise’,则会引发 ValueError。 如果将其设置为 ‘ignore’,则字典中的其他键值将被忽略。 所有其他可选或关键字参数都传递给底层的 writer 实例。

import csv

with open("data.csv", 'r') as f:
    data = csv.reader(f)
    for line in data:
        print(line)

# 执行结果
"""
['username', 'password', 'assert_text']
['admin1', 'psd1', 'ggg']
['admin2', 'psd2', 'hhh']
['admin3', 'psd3', 'jjj']
"""

##DictReader()

import csv

with open("data.csv", 'r') as f:
    # data = csv.reader(f)
    data = csv.DictReader(f)
    for line in data:
        print(dict(line))
# 执行结果
"""
{'username': 'admin1', 'password': 'psd1', 'assert_text': 'ggg'}
{'username': 'admin2', 'password': 'psd2', 'assert_text': 'hhh'}
{'username': 'admin3', 'password': 'psd3', 'assert_text': 'jjj'}
"""

codecs模块

在数据文件中不可避免会使用中文,codecs是python的标准的模块编码和解码器。
因为文件中我们一般会在第一行定义字段名,在读取时需要跳过,itertools提供了用于操作迭代对象的函数,即islice()函数,它可以返回一个迭代器第一个参数指定迭代对象,第二个参数指定开始迭代的位置,第三个参数表示结束位。

import csv
import codecs
import itertools

# csv内容如下
"""
username,password,assert_text
admin1,psd1,ggg
admin2,psd2,hhh
admin3,psd3,中文
"""

with codecs.open("data.csv", 'r', 'utf_8_sig') as f:
    data = csv.reader(f)
    for line in itertools.islice(data, 1, None):  # 跳过头
        print(line)
"""
['admin1', 'psd1', 'ggg']
['admin2', 'psd2', 'hhh']
['admin3', 'psd3', '中文']
"""

YAML

YAML是一种直观的能够被计算机识别的数据序列化格式,容易阅读,并且容易和脚本语言交互。YAML类似于XML,但是语法较XML简单的多:JSON,YAML可以写成规范化的配置文件。此外,不管做Web自动化测试还是接口自动化测试,都可以使用YAML来管理测试数据。

安装

pip install pyyaml

YAML文档可参考:YAML Documentation

YAML

建立data.yaml文件

userNull:
  username: ""
  password: ""
  assertText: ""
passNull:
  username1: "admin"
  password1: "12345"
  assertText1: "请输入账号密码"

读取yaml文件

from yaml import load, FullLoader

def readYaml():
    """获取所有的yaml数据"""
    f = open("data.yaml", 'r', encoding="utf-8")
    data = load(f, Loader=FullLoader)
    f.close()
    return data


if __name__ == '__main__':
    print(readYaml())

Excel结合DDT

import ddt
import yaml
import unittest
import time
from selenium import webdriver


@ddt.ddt
class TestBaidu(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()

    def baiduSearch(self, search_text):
        self.driver.get("http://www.baidu.com")
        self.driver.find_element_by_id("kw").send_keys(search_text)
        self.driver.find_element_by_id("su").click()
        time.sleep(3)

    @ddt.file_data("ddt_data.yaml")  # 中文乱码
    @ddt.unpack
    def test_case(self, case):
        print("正在测试----" + case[0]["search_text"] + "------------")
        search_text = case[0]['search_text']
        assert_text = case[1]['assertText']
        self.baiduSearch(search_text)
        self.assertEqual(self.driver.title, assert_text)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == "__main__":
    unittest.main()

parameterized

参数化,是对unittest单元测试框架的参数化扩展。它可以结合unittest单元测试框架进行一些参数化策略的应用。

安装

pip install parameterized

parameterized在自动化测试中应用

import unittest

import time
from selenium import webdriver
from parameterized import parameterized


class TestBaiduSearch(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()

    def baidu_search(self, url, search_text):
        self.driver.get(url)
        self.driver.find_element_by_id("kw").send_keys(search_text)
        self.driver.find_element_by_id("su").click()
        time.sleep(3)

    @parameterized.expand([
        ("http://www.baidu.com", "selenium", "selenium_百度搜索"),
        ("http://www.baidu.com", "ddt", "ddt_百度搜索"),
        ("http://www.baidu.com", "parameterized", "selenium_百度搜索")
    ])
    def test_baidu(self, url, search_text, assert_text):
        self.baidu_search(url, search_text)
        self.assertEqual(self.driver.title, assert_text)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == "__main__":
    unittest.main()

@parameterized.expand()方法用来存放多组测试数据,每组测试数据都会作为实参传递给测试用例中的url、search_text和assert_text。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值