Python框架之UnitTest
admin
2024-02-07 10:32:17
0

unittest 是python 的单元测试框架,unittest 单元测试提供了创建测试用例,测试套件以及批量执行的方案, unittest 在安装pyhton 以后就直接自带了,直接import unittest 就可以使用,测试人员用UnitTest来做自动化测试,即管理和执行用例。
一、UnitTest基本组成
测试人员使用此框架的原因:能够组织多个用例去执行、提供丰富的断言方法、能够生成测试报告。
1、测试发现:从多个py文件中收集并加载测试用例;
2、测试执行:将测试用例按照一定的顺序和条件去执行并生成结果;
3、测试判断:通过断言去判断结果是否正确;
4、测试报告:统计测试进度,通过率,生成报告;
UnitTest的组成部分:
1、TestCase:用来写测试用例
2、TestSuite:测试套件,用来组装测试用例,即打包TestCase
3、TestRunner:测试执行,用来执行TestSuite
4、TestLoader:测试加载,对TestSuite功能的补充,用来组装TestCase
5、Fixture:测试夹具,是一种代码结构,前置方法和后置方法
TestCase测试用例
步骤:
(1)导包 unittest
(2)定义测试类,只需要继承unittest.TestCase类就是测试类
(3)书写测试方法,方法名必须以test开头
(4)执行代码

(1)导包 unittest

import unittest

(2)定义测试类,只需要继承unittest.TestCase类就是测试类

class TestDemo(unittest.TestCase):
# (3)书写测试方法,方法名必须以test开头
def test_method1(self):
print(‘测试方法一’)

def test_method2(self):print('测试方法二')

(4)执行代码

4.1在类名或者方法名后面右键运行。类名后面运行:执行类中所有的测试方法;在方法名后面,只执行当前的测试方法

4.2 在主程序中使用unittest.main()来执行

if name == ‘main’:
unittest.main()
复制代码
TestSuite和TestRunner
TestSuite(测试套件),用来组装测试用例,将多个测试用例脚本集合在一起。
使用步骤:

实例化测试套件:suite = unittest.TestSuite()

添加用例:suite.addTest(ClassName(“MethodName”))
ClassName:为类名;MethodName:为方法名

添加扩展:suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(ClassName))

添加扩展:suite.addTest(unittest.makeSuite(测试类名))
把指定ClassName类中的测试用例全部添加到测试套件中
案例: 两个用例文件testcase1.py和testcase2.py
testcase1.py文件中的代码为;

import unittest

class TestDemo1(unittest.TestCase):

def test_method1(self):print('测试方法1-1')def test_method2(self):print('测试方法1-2')

复制代码
testcase2.py文件中的代码为;

1、导包

import unittest

2、定义测试类,继承unittest.TestCase就是测试类

class TestDemo2(unittest.TestCase):
# 3、写测试方法,方法中的代码是真正的测试用例代码,方法名必须以test开头
def test_method1(self):
print(‘测试方法2-1’)

def test_method2(self):print('测试方法2-2')

复制代码
测试套件和执行testsuite_testrunner.py文件中代码为(第一种添加测试套件的方法):

1、导包

import unittest
from testcase1 import TestDemo1

2、实例化套件对象unittest.TestSuite()

from testcase2 import TestDemo2

suite = unittest.TestSuite()

3、添加用例方法

3.1 套件对象.addTest(测试类名(‘测试方法名’))

suite.addTest(TestDemo1(‘test_method1’))
suite.addTest(TestDemo1(‘test_method2’))
suite.addTest(TestDemo2(‘test_method1’))
suite.addTest(TestDemo2(‘test_method2’))

4、实例化、执行对象unittest.TextTestRunner()

runner = unittest.TextTestRunner()

5、执行 执行对象

runner.run(suite)
复制代码
执行结果:

附加:测试套件和执行testsuite_testrunner.py文件中代码为(第二种添加测试套件的方法):

1、导包

import unittest
from testcase1 import TestDemo1

2、实例化套件对象unittest.TestSuite()

from testcase2 import TestDemo2

suite = unittest.TestSuite()

3、添加用例方法

3.2 添加整个测试类,套件对象.addTest(unittest.makeSuite(测试类名))

suite.addTest(unittest.makeSuite(TestDemo1))
suite.addTest(unittest.makeSuite(TestDemo2))

4、实例化、执行对象unittest.TextTestRunner()

runner = unittest.TextTestRunner()去执行

()

5、执行 执行对象

runner.run(suite)
复制代码
TestLoader 测试加载
用来加载TestCase到TestSuite中,即加载满足条件的测试用例,并把测试用例封装成测试套件
suite = unittest.TestLoader().discover(test_dir, pattern=‘test*.py’)
自动搜索指定目录下指定开头的.py文件,并将查找到的测试用例组装到测试套件
test_dir: 为指定的测试用例的目录
pattern:为查找的.py文件的格式,默认为’test*.py’
import unittest

suite = unittest.TestLoader().discover(‘E:\pythonProject\pythonUnitTest’, ‘testcase*.py’)
runner = unittest.TextTestRunner()
runner.run(suite)
复制代码
Fixture:测试夹具
测试夹具是一种代码结构。Fixture控制级别:方法级别、类级别、模块级别(了解即可)。
方法级别(在每个测试用例执行前后都会自动调用,方法名是固定的):
使用方式:
初始化(前置处理):def setUp(self) --> 开始自动执行
销毁(后置处理):def tearDown(self) --> 最后自动执行
类级别(在类中,所有的测试方法执行前后,会自动执行一次,方法名是固定的):
使用方式:
初始化(前置处理):
@classmethod

def setUpClass(cls):
复制代码
销毁(后置处理):
@classmethod

def tearDownClass(cls):
复制代码
方法名固定的本质是:继承unittest的方法,并且进行覆盖是重写
模块级别 [了解]
运行于整个模块的始末,即:整个模块只会运行一次setUpModule和tearDownModule
使用方式:
初始化(前置处理):def setUpModule()
销毁(后置处理):def tearDownModule()
案例分析:
登录功能的测试用例:
(1)打开浏览器–1次
(2)打开网页,点击登录–每次
(3)输入用户名密码验证码1,点击登录–每次,测试方法1
(4)关闭网页–每次

(2)打开网页,点击登录–每次
(3)输入用户名密码验证码2,点击登录–每次,测试方法2
(4)关闭网页–每次

(2)打开网页,点击登录–每次
(3)输入用户名密码验证码3,点击登录–每次,测试方法3
(4)关闭网页–每次
以上案例分析有很多重复的代码,我们可以使用测试夹具优化代码
import unittest

class TestLogin(unittest.TestCase):
def setUp(self) -> None:
print(‘2、打开网页,点击登录–每次–前置’)

def tearDown(self) -> None:print('4、关闭网页--每次--后置')@classmethod
def setUpClass(cls) -> None:print('1、打开浏览器--类前置')@classmethod
def tearDownClass(cls) -> None:print('5、关闭浏览器--类后置')def test_1(self):print('3、输入用户名密码验证码1,点击登录--每次,测试方法1')def test_2(self):print('3、输入用户名密码验证码1,点击登录--每次,测试方法2')def test_3(self):print('3、输入用户名密码验证码1,点击登录--每次,测试方法3')

复制代码

二、断言
断言:让程序代替人为判断测试程序执行结果是否符合预期结果的过程
常用的UnitTest断言方法

对于自动化测试人员来说,重点掌握:
(1)assertEqual(预期结果,实际结果)
判断预期结果和实际结果是否相等,如果相等,用例通过,如果不相等,抛出异常,用例不通过
(2)assertIn(预期结果,实际结果)
判断预期结果是否包含在实际结果中,如果存在,用例通过,如果不存在,抛出异常,用例不通过
import unittest

class TestAssert(unittest.TestCase):
def test_equal_1(self):
self.assertEqual(10, 10) # 用例通过

def test_assert_2(self):self.assertEqual(10, 20)    # 用例不通过def test_in01(self):self.assertIn('admin', '欢迎admin登录系统!')  # 包含...用例通过def test_in02(self):self.assertIn('addddmin', '欢迎登录系统!')  # 不包含...用例不通过

复制代码
案例:
工具类中有一个求和的测试方法:
def add(a, b):
sum = a+b
return sum
复制代码
使用断言测试该求和方法是正确:
import unittest
from tools import add

class TestAdd(unittest.TestCase):
def test_1(self):
self.assertEqual(3, add(1, 2))

def test_1(self):self.assertEqual(5, add(3, 2))def test_1(self):self.assertEqual(7, add(4, 3))def test_1(self):self.assertEqual(13, add(8, 5))

复制代码
三、参数化
通过参数的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行。
unittest测试框架,本身不支持参数化,但是可以通过安装unittest扩展插件parameterized来实现。
安装parameterized:pip install parameterized

参数化实现
导包:from parameterized import parameterized
使用@parameterized.expand装饰器可以为测试函数的参数进行参数化
我们继续对以上的求和方法使用参数化的方式进行测试,代码运行的时候,可以使用多种方式运行,比如:直接右键运行、main方法运行、suite运行等
import unittest
from tools import add
from parameterized import parameterized

data = [(1, 1, 2), (2, 3, 5), (3, 4, 7), (10, 11, 21)]

class TestAdd(unittest.TestCase):
@parameterized.expand(data)
def test_1(self, a, b, expect):
self.assertEqual(expect, add(a, b))

if name == ‘main’:
unittest.main()
复制代码
扩展:
我们将参数化的测试数据保存在json文件中,如下;
[
[1, 1, 2],
[2, 3, 5],
[3, 4, 7],
[10, 11, 21]
]
复制代码

读取json文件中的数据:
import json

def build_add_data():
with open(‘add_data.json’,encoding=‘utf-8’)as f:
data = json.load(f)

return data

复制代码

将读取的json文件中的数据当作参数传到测试用例中:
import unittest

from read_json import build_add_data
from tools import add
from parameterized import parameterized

class TestAdd(unittest.TestCase):
@parameterized.expand(build_add_data)
def test_1(self, a, b, expect):
self.assertEqual(expect, add(a, b))

if name == ‘main’:
unittest.main()
复制代码

以上就是将数据保存在json文件中实现参数化,json文件中的数据复杂的时候,我们可以使用如下方式定义(并且推荐此方法):

此时读取json文件的方法需要修改为:
def build_add_data():
with open(‘add_data.json’, encoding=‘utf-8’)as f:
data_list = json.load(f)
new_list = []
for data in data_list:
a = data.get(‘a’)
b = data.get(‘b’)
expect = data.get(‘expect’)
new_list.append((a, b, expect))
return new_list
复制代码
四、生成HTML测试报告
安装:pip install HTMLTestReport
测试报告生成步骤:
(1)导包
(2)封装测试套件
(3)实例化HTMLTestReport对象
report = HTMLTestReport(file_path, [title], [description])
参数说明:
file_path:测试报告文件路径
title:可选参数,为报告标题,如XXX测试报告
description:可选参数,为报告描述信息
(4)执行测试套件report.run(suite)
继续使用上述示例,针对求和的方法进行测试报告的生成
import unittest
from htmltestreport import HTMLTestReport
from testadd_paramterized import TestAdd

测试套件

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAdd))

运行对象

runner = HTMLTestReport(‘test_add_report.html’, ‘求和运算测试报告’, ‘这是报告的描述!’)
runner.run(suite)
复制代码
运行之后将生成的html报告文件,使用浏览器打开,查看生成的报告,报告中执行失败的用例是故意写错,用来查看执行失败的情况:

使用绝对路径保存测试报告
在实际项目中往往会出现文件找不到的情况,此时需要使用绝对路径。
步骤:
(1)在项目的根目录中创建一个配置文件(app.py或者config.py)
(2)在这个文件中获取项目的目录,在其他代码中拼接起来,完成绝对路径
获取当前文件的绝对路径:abspath = os.path.abspath(file)
获取文件路径的目录名称:dirname = os.path.dirname(filepath)
案例:
app.py中的代码内容
import os

获取当前文件的绝对路径

path1 = os.path.abspath(file)
print(path1)

获取文件路径的目录名称

path2 = os.path.dirname(path1)
print(path2)

实际中直接合起来写就行

BASE_DIR = os.path.dirname(os.path.abspath(file))

if name == ‘main’:
print(BASE_DIR)
复制代码
运行结果:

代码中的使用

测试套件

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAdd))

运行对象

report_path = app.BASE_DIR + “test_add_report.html” # 报告的路径
runner = HTMLTestReport(report_path, ‘求和运算测试报告’, ‘这是报告的描述!’)
runner.run(suite)
复制代码
五、测试用例跳过
直接将测试函数标记成跳过@unittest.skip(‘代码未完成’)
根据条件判断测试函数是否跳过@unittest.skipIf(condition, reason)
示例:
import unittest

ver = 10

class TestSkip(unittest.TestCase):
@unittest.skip(‘此用例跳过,这是跳过的原因’)
def test_1(self):
print(‘测试方法一’)

@unittest.skipIf(ver >= 10, '迭代版本大于等于10,此用例跳过')
def test_2(self):print('测试方法二')def test_3(self):print('测试方法三')

if name == ‘main’:
unittest.main()

相关内容

热门资讯

罕见9连跌,历史第二次!上证5... 最近,A股市场“冰火两重天”,有色金属、国防军工、电子等行业轮番走强,而大盘蓝筹却持续阴跌。 截至1...
见证历史!纽约白银期货、伦敦现... 1月23日晚,又见证历史了。1月23日晚间,纽约白银期货、伦敦现货白银双双突破100美元历史性关口!...
原创 淘... 当电商转向价值竞争,好服务成了好增长的密钥。 原创ⓒ新熵 新消费组 作者丨栀子 编辑丨九黎 进入20...
晚上9点后别做这几件事!一个好... 太原龙城中医医院科普:对于肺结节人群而言,除了饮食、运动等日常养护,优质睡眠的重要性往往被忽视。中医...
破“7”!人民币汇率中间价调升... 北京商报讯(记者 廖蒙)1月23日,中国人民银行授权中国外汇交易中心公布,当日银行间外汇市场人民币汇...
原创 异... 在探讨异性交往的微妙关系时,我们不得不提到一个至关重要的话题——男性回家后的行为模式。高情商的女性往...
和讯投顾王海洋:大盘震荡收星,... 1月23日,和讯投顾王海洋表示,大盘震荡收星,平均股价再创新高。昨日曾提到,今日大盘大概率会再次触摸...
马斯克成为科技行业“风向标” ... 来源:@投中网微博 【马斯克成为科技行业“风向标” 带火汽车航天光伏三大领域】作为科技行业标杆与全球...
凝“新”聚力│发挥“联”优势 ... 前 言 为深入贯彻落实习近平总书记同全国妇联新一届领导班子成员集体谈话时提出的“加大在新经济组织、新...
【日常消费品ETF收涨约0.8... 【日常消费品ETF收涨约0.8%,领跑美股行业ETF,半导体ETF跌约0.7%,银行业ETF跌超3....
每月最高800元!中度以上失能... 民政部、财政部日前印发通知,从今年1月1日起,面向中度以上失能老年人发放养老服务消费补贴的政策在全国...
龙虎榜揭秘!大牛股背后资金动向... 龙虎榜揭秘。 近期A股市场整体波动较为平稳,但不少个股波动剧烈,甚至连续涨停或连续跌停,近日的龙虎榜...
原创 全... 全球都在用美元? 中国偷偷搞了个大动作! 美元占全球支付50%时人民币干了啥? 你可能不知道的是,当...
我国银行理财市场规模突破33万... 银行业理财登记托管中心1月23日发布的《中国银行业理财市场年度报告(2025年)》显示,截至2025...
最高分红率35%!上市银行春节... 随着春节临近,上市银行2025年中期分红逐渐进入尾声。 1月23日,华夏银行、渝农商行迎来2025年...
蔡英丽医生:帕金森患者麻醉注意... 帕金森病是中老年人常见的神经系统退行性疾病,随着病情进展,不少患者可能需要接受各类手术,而麻醉环节的...
原创 利... 朋友们,今天A股发生了一件挺有意思的事:在地面光伏行业不少公司还在为亏损发愁的时候,一个叫做“太空光...
二游王战之局,鹰角先下一城 2026二游王战的启幕来了。1月22日,鹰角《明日方舟:终末地》(以下简称终末地)正式公测,和我们预...
大润发首次跨界合作蛋仔派对,以... 2026年春节前夕,高鑫零售旗下核心品牌大润发与国民级游戏IP《蛋仔派对》正式达成深度跨界合作,共同...
2025年基金4季报重仓股全扫... 随着基金2025年4季报基本披露完毕,记者注意到,截至去年底,基金的重仓股发生了比较明显的变化,有5...