单元测试和unittest框架(超详细总结)

news2024/11/16 1:36:51

🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,薪资嘎嘎涨

单元测试的定义

1. 什么是单元测试?

单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类,一般是开发来做的,按照测试阶段来分,就是单元测试、集成测试、系统测试以及验收测试。

2.为什么要做单元测试?

📕 单元测试之后,才是集成测试,单个单个的功能模块测试通过之后,才能把单个功能模块集成起来做集成测试,为了从底层发现bug,单元测试时可以减少合成后出现的问题。
📒 越早发现bug越好,这样可以早点发现问题,不然问题累计到后面,很可能会因为一个做错了而导致整个模块甚至更大范围的推倒重来,对于时间和经费来说,是非常浪费的!
📘 对于测试来说,单元测试就是为了执行用例,输入测试数据--》输出测试结果

unittest框架及原理

做过自动化测试的同学应该都知道python中的unittest框架,它是python自带的一套测试框架,学习起来也相对较容易,unittest框架最核心的四个概念:

​ 🍊 test case:就是我们的测试用例,unittest中提供了一个基本类TestCase,可以用来创建新的测试用例,一个TestCase的实例就是一个测试用例;unittest中测试用例方法都是以test开头的,且执行顺序会按照方法名的ASCII值排序。

​ 🍅 test fixure:测试夹具,用于测试用例环境的搭建和销毁。即用例测试前准备环境的搭建(SetUp前置条件),测试后环境的还原(TearDown后置条件),比如测试前需要登录获取token等就是测试用例需要的环境,运行完后执行下一个用例前需要还原环境,以免影响下一条用例的测试结果。

​ 🍋 test suite:测试套件,用来把需要一起执行的测试用例集中放到一块执行,相当于一个篮子。我们可以使用TestLoader来加载测试用例到测试套件中。

​ 🍇 test runner:用来执行测试用例的,并返回测试用例的执行结果。它还可以用图形或者文本接口,把返回的测试结果更形象的展现出来,如:HTMLTestRunner。

unittest的断言

在python基础中,我们有讲过一个assert断言,使用方法比较简单,即assert 表达式, 提示信息,而unittest框架中也提供了一个自带的断言方式,主要有以下几种:

如果断言失败即不通过就会抛出一个AssertionError断言错误,成功则标识为通过,以上几种方式都有一个共同点,就是都有一个msg参数(表中只列了一个,其实都有),默认是None,即msg = None,如果指定msg参数的值,则将该信息作为失败的错误信息返回。

TestCase测试用例

编写测试用例前,我们需要建一个测试类继承unittest里面的TestCase类,继承这个类之后我们才是真正的使用unittest框架去写测试用例,编写测试用例的步骤如下:

  • 导入unittest模块
  • 创建一个测试类,并继承unittest.TestCase()
  • 定义测试方法,方法名必须以test_开头
  • 调用unittest.main()方法来运行测试用例,unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行

下面以注册功能为例,这个register.py就是注册功能的代码,没有前端界面,功能比较简单,只是方便用于演示,直接导入就可以使用。

# register.py
users = [{'username': 'test', 'password': '123456'}]


def register(username, password1, password2):

    if not all([username, password1, password2]):
        return {"code": 0, "msg": "所有参数不能为空"}
    # 注册功能
    for user in users:
        if username == user['username']:
            return {"code": 0, "msg": "该用户名已存在!"}
    else:
        if password1 != password2:
            return {"code": 0, "msg": "两次密码输入不一致!"}
        else:
            if 6 <= len(username) >= 6 and 6 <= len(password1) <= 18:
                users.append({'username': username, 'password': password2})
                return {"code": 1, "msg": "注册成功"}
            else:
                return {"code": 0, "msg": "用户名和密码必须在6-18位之间"}

下面是编写测试用例例子:

# test_register.py
import unittest
from register import register   # 导入被测试的代码


class TestRegister(unittest.TestCase):
    """注册接口测试用例类"""

    def test_register_success(self):
        """注册成功"""
        data = ("mikitest", "miki123", "miki123")   # 测试数据
        expected = {"code": 1, "msg": "注册成功"}   # 预期结果
        result = register(*data)    # 把测试数据传到被测的代码,接收实际结果
        self.assertEqual(expected, result)  # 断言,预期和实际是否一致,一致即用例通过

    def test_username_isnull(self):
        """注册失败-用户名为空"""
        data = ("", "miki123", "miki123")
        expected = {"code": 0, "msg": "所有参数不能为空"}
        result = register(*data)
        self.assertEqual(expected, result)

    def test_username_lt6(self):
        """注册失败-用户名大于18位"""
        data = ("mikitestmikitestmikitest", "miki123", "miki123")
        expected = {"code": 0, "msg": "用户名和密码必须在6-18位之间!"}
        result = register(*data)
        self.assertEqual(expected, result)    # 这条用例应该是不通过的,注册代码bug

    def test_pwd1_not_pwd2(self):
        """注册失败-两次密码不一致"""
        data = ("miki123", "test123", "test321")
        expected = {"code": 0, "msg": "两次密码输入不一致!"}
        result = register(*data)
        self.assertEqual(expected, result)


# 如果直接运行这个文件,需要使用unittest中的main函数来执行测试用例
if __name__ == '__main__':
    unittest.main()

测试用例运行结果如下,一共4条用例,其中通过3条,不通过1条,不通过的是本身注册代码的bug。

Testing started at 21:58 ...
C:\software\python\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.3\helpers\pycharm\_jb_unittest_runner.py" --target test_register.TestRegister
Launching unittests with arguments python -m unittest test_register.TestRegister in D:\learn\python_test


{'code': 1, 'msg': '注册成功!'} != {'code': 0, 'msg': '用户名和密码必须在6-18位之间!'}

Expected :{'code': 0, 'msg': '用户名和密码必须在6-18位之间!'}
Actual   :{'code': 1, 'msg': '注册成功!'}
<Click to see difference>

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.3\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
    old(self, first, second, msg)
  File "C:\software\python\lib\unittest\case.py", line 839, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\software\python\lib\unittest\case.py", line 1138, in assertDictEqual
    self.fail(self._formatMessage(msg, standardMsg))
  File "C:\software\python\lib\unittest\case.py", line 680, in fail
    raise self.failureException(msg)
AssertionError: {'code': 0, 'msg': '用户名和密码必须在6-18位之间!'} != {'code': 1, 'msg': '注册成功!'}
- {'code': 0, 'msg': '用户名和密码必须在6-18位之间!'}
+ {'code': 1, 'msg': '注册成功!'}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\software\python\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\software\python\lib\unittest\case.py", line 615, in run
    testMethod()
  File "D:\learn\python24\python_base\day13_task\test_register.py", line 36, in test_username_lt6
    self.assertEqual(expected, result)


Ran 4 tests in 0.007s

FAILED (failures=1)

Process finished with exit code 1

Assertion failed

TestFixure测试夹具

unittest的测试夹具有两种使用方式,一种是以测试方法为维度的setUp()和tearDown(),一种是以测试类为维度的setUpClass()和tearDownClass()。以注册功能为例,但这个注册代码比较简单,没有真正需要用到测试夹具的地方,因此这只是个用法演示。

# test_register.py
import unittest
from register import register   # 导入被测试的代码


class TestRegister(unittest.TestCase):
    """注册接口测试用例类"""
    
    def setUp(self):
        # 每条用例执行之前都会执行
        print("用例{}开始执行--".format(self))

    def tearDown(self):
        # 每条用例执行之后都会执行
        print("用例{}执行结束--".format(self))

    @classmethod    # 指明这是个类方法以类为维度去执行的
    def setUpClass(cls):
        # 整个测试用例类中的用例执行之前,会先执行此方法
        print("-----setup---class-----")

    @classmethod
    def tearDownClass(cls):
        # 整个测试用例类中的用例执行完之后,会执行此方法
        print("-----teardown---class-----")

    def test_register_success(self):
        """注册成功"""
        data = ("mikitest", "miki123", "miki123")   # 测试数据
        expected = {"code": 1, "msg": "注册成功!"}   # 预期结果
        result = register(*data)    # 把测试数据传到被测的代码,接收实际结果
        self.assertEqual(expected, result)  # 断言,预期和实际是否一致,一致即用例通过

    def test_username_isnull(self):
        """注册失败-用户名为空"""
        data = ("", "miki123", "miki123")
        expected = {"code": 0, "msg": "所有参数不能为空!"}
        result = register(*data)
        self.assertEqual(expected, result)


# 如果直接运行这个文件,需要使用unittest中的main函数来执行测试用例
if __name__ == '__main__':
    unittest.main()

运行结果:

Testing started at 22:19 ...
C:\software\python\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.3\helpers\pycharm\_jb_unittest_runner.py" --path D:/learn/python/test_register.py
Launching unittests with arguments python -m unittest D:/learn/python/test_register.py in D:\learn\python
-----setup---class-----用例test_pwd1_not_pwd2 (test_register.RegisterTestCase)开始执行--
用例test_pwd1_not_pwd2 (test_register.RegisterTestCase)执行结束--
用例test_register_success (test_register.RegisterTestCase)开始执行--
用例test_register_success (test_register.RegisterTestCase)执行结束--
用例test_username_isnull (test_register.RegisterTestCase)开始执行--
用例test_username_isnull (test_register.RegisterTestCase)执行结束--
用例test_username_lt6 (test_register.RegisterTestCase)开始执行--
用例test_username_lt6 (test_register.RegisterTestCase)执行结束--
-----teardown---class-----

Ran 4 tests in 0.003s

OK

Process finished with exit code 0

TestSuite测试套件

unittest.TestSuite()类来表示一个测试用例集,把需要执行的用例类或模块存到一起,常用的方法如下:

🍊 unittest.TestSuite()

  • addTest():添加单个测试用例方法
  • addTest([..]):添加多个测试用例方法,方法名存在一个列表

🍊 unittest.TestLoader()

  • loadTestsFromTestCase(测试类名):添加一个测试类
  • loadTestsFromModule(模块名):添加一个模块
  • discover(测试用例的所在目录):指定目录去加载,会自动寻找这个目录下所有符合命名规则的测试用例
# run_test.py,与test_register.py、register.py同一目录下
import unittest
import test_register

# 第一步,创建一个测试套件
suite = unittest.TestSuite()

# 第二步:将测试用例,加载到测试套件中
# 方式1,添加单条测试用例
# case = test_register.TestRegister("test_register_success")    # 创建一个用例对象,注意:通过用例类去创建测试用例对象的时候,需要传入用例的方法名(字符串类型)
# suite.addTest(case)    # 添加用例到测试套件中

# 方式2,添加多条测试用例
# case1 = test_register.TestRegister("test_register_success")
# case2 = test_register.TestRegister("test_username_isnull")
# suite.addTest([case1, case2])    # 添加用例到测试套件中

# 方式3,添加一个测试用例类
# loader = unittest.TestLoader()    # 创建一个加载对象
# suite.addTest(loader.loadTestsFromTestCase(test_register.TestRegister))

# 方式4,添加一个模块
loader = unittest.TestLoader()    # 创建一个加载对象
suite.addTest(loader.loadTestsFromModule(test_register))

# 方式5,指定测试用例的所在的目录路径,进行加载
# loader = unittest.TestLoader()
# suite.addTest(loader.discover(r"d:\learn\python"))

通常我们使用方式4、5比较多,你可以根据实际情况来运用。其中方式5,还可以自定义匹配规则,默认是会寻找目录下test*.py文件,即所有以test开头命名的py文件,自定义如下:

loader = unittest.TestLoader()
suite.addTest(loader.discover(start_dir = r"d:\learn\python", pattern="test_case*.py"))        # 匹配规则:所有以test_case开头的

TestRunner执行用例​

test runner顾名思义就是用来执行测试用例的,并且可以生成相应的测试报告。测试报告有两种展示形式,一种是text文本,一种是html格式。

​html格式的就是HTMLTestRunner了,HTMLTestRunner是 Python 标准库的 unittest 框架的一个扩展,它可以生成一个直观清晰的 HTML 测试报告。使用的前提就是要下载 HTMLTestRunner.py,下载完后放在python的安装目录下的scripts目录下即可。

​text文本相对于html来说过于简陋,与控制台输出的没有什么区别,也几乎没有人使用,这里不作演示,使用方法是一样的。我们结合前面的测试套件来演示一下如何生成html格式的测试报告:

# run_test.py,与test_register.py、register.py同一目录下
import unittest
import test_register
from HTMLTestRunner import HTMLTestRunner

# 创建测试套件
suite = unittest.TestSuite()

# 通过模块加载测试用例
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromModule(test_register))

# 创建测试运行程序启动器
runner = HTMLTestRunner(stream=open("report.html", "wb"),  # 打开一个报告文件,将句柄传给stream
                        tester="miki",                    # 报告中显示的测试人员
                        description="注册接口测试报告",        # 报告中显示的描述信息
                        title="自动化测试报告")                 # 报告的标题

# 使用启动器去执行测试套件里的用例
runner.run(suite)

相关参数说明:

stream:指定输出的方式
tester:报告中要显示的测试人员的名字
description:报告中要显示的面熟信息
title:测试报告的标题
verbosity :表示测试报告信息的详细程度,一共三个值,默认是2

  • 0 (静默模式):你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
  • 1 (默认模式):类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
  • 2 (详细模式):测试结果会显示每个测试用例的所有相关的信息

运行完毕,你会发现你的项目目录下已经生成了一个report.html文件,在浏览器中打开,就可以查看测试报告了。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

​这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子烟智能化创新体验:WTK6900P语音交互芯片方案,融合频谱计算、精准语音识别与流畅音频播报

一&#xff1a;开发背景 在这个科技日新月异的时代&#xff0c;每一个细节的创新都是对传统的一次超越。今天&#xff0c;我们自豪地宣布一项革命性的融合——将先进的语音识别技术与电子烟相结合&#xff0c;通过WTK6900P芯片的卓越性能&#xff0c;为您开启前所未有的个性化…

【有啥问啥】摄像头成像质量量化标准解读与测试方法

摄像头成像质量量化标准解读与测试方法 在自动驾驶和智能驾驶舱领域&#xff0c;摄像头是关键的感知设备&#xff0c;直接关系到系统的环境感知能力。为确保摄像头在实际应用中表现出色&#xff0c;需明确了解其成像质量标准和测试方法。本文将围绕成像质量的核心指标、测试方…

【机器学习(九)】分类和回归任务-多层感知机 (MLP) -Sentosa_DSML社区版

文章目录 一、算法概念二、算法原理&#xff08;一&#xff09;感知机&#xff08;二&#xff09;多层感知机1、隐藏层2、激活函数sigma函数tanh函数ReLU函数 3、反向传播算法 三、算法优缺点&#xff08;一&#xff09;优点&#xff08;二&#xff09;缺点 四、MLP分类任务实现…

PCL addLine可视化K近邻

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xff09; 一、概述 本文将介绍如何使用PCL库中…

前端实用工具(二):编程规范化解决方案

目录 本地代码规范化工具 代码检测工具ESLint 代码格式化工具Prettier 远程代码规范化工具 远程提交规范化工具commitizen 提交规范检验工具commitlint husky 什么是git hooks commitlint安装 husky安装 检测代码提交规范 ESLint husky 自动修复格式错误lint-staged…

前缀和与差分(二维)

二维前缀和 下面是一个二维数组&#xff0c;我们要求&#xff08;1&#xff0c;1&#xff09;到&#xff08;2&#xff0c;2&#xff09;区间内的所有元素的和&#xff0c;最原始的方法就是遍历每个元素然后一个一个加起来&#xff0c;此时时间复杂度为O(n*m)。 我们之前学过…

华为---代理ARP工作过程示例分析

目录 1. 示例场景 2. 基本配置 3. 配置代码 4. 测试验证 5. 抓包分析 5.1 在代理ARP环境下PC1和PC2通信分析 5.2 取消代理ARP环境下PC1和PC2通信分析 【1】取消R1路由器GE 0/0/1端口ARP代理 【2】取消R2路由器GE 0/0/1端口ARP代理 1. 示例场景 如上图所示&#xff0c;…

Python 中的方法解析顺序(MRO)

在 Python 中&#xff0c;MRO&#xff08;Method Resolution Order&#xff0c;方法解析顺序&#xff09;是指类继承体系中&#xff0c;Python 如何确定在调用方法时的解析顺序。MRO 决定了在多继承环境下&#xff0c;Python 如何寻找方法或属性&#xff0c;即它会根据一定规则…

Ceph 基本架构(一)

Ceph架构图 Ceph整体组成 Ceph 是一个开源的分布式存储系统&#xff0c;设计用于提供优秀的性能、可靠性和可扩展性。Ceph 的架构主要由几个核心组件构成&#xff0c;每个组件都有特定的功能&#xff0c;共同协作以实现高可用性和数据的一致性。 以下是 Ceph 的整体架构及其…

【PyTorch】autograd与逻辑回归

autograd – 自动求导系统 torch.autograd autograd torch.autograd.backward 功能&#xff1a;自动求取梯度 tensor&#xff1a;用于求导的张量&#xff0c;如lossretain_graph&#xff1a;保存计算图create_graph&#xff1a;创建导数计算图&#xff0c;用于高阶求导gra…

mac安装JetBtains全家桶新版本时报错:Cannot start the IDE

mac安装JetBtains全家桶新版本时报错&#xff1a;Cannot start the IDE 前言报错信息解决方法 前言 作者使用的是Mac电脑&#xff0c;最近想要更新JetBrains相关工具的软件版本&#xff0c;但是在安装时突然报错&#xff0c;导致安装失败&#xff0c;现在将报错信息以及解决方…

API应用安全风险倍增,F5助企业赢得关键安全挑战

如今&#xff0c;API应用安全的重要性与日俱增&#xff0c;但其复杂性也达到前所未有的程度。F5公司在亚太地区发布全新研究报告《2024策略洞察&#xff1a;亚太地区API安全报告》&#xff08;以下简称为“报告”&#xff09;&#xff0c;指出亚太地区企业正愈加依赖基于人工智…

Linux 进程2

环境变量 再Linux操作系统中一切皆文件&#xff0c;这个环境变量自然也是一个文件&#xff0c;它的作用是辅助我们使用操作系统还可以辨识我们是什么用户(一般用户&#xff0c;root用户)。 env是读取完整环境变量的指令&#xff0c;里面记录了许多我登录操作系统所用的用户的信…

一文说清楚ETL与Kafka如何实现集成

ETL与Kafka为何需要集成? 随着企业对实时流数据的处理要求越来越高&#xff0c;很多企业都把实时流数(日志、实时CDC采集数据、设备数据…)先推入到kafka中&#xff0c;再通过ETL对kafka中的数据进行消费通过ETL强大的数据的转换、清洗功能来进行数据的集成与分发。 实时数据…

table表格,让thead固定,tbody内容滚动,关键是都对齐的纯css写法

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f…

地信、测绘、遥感、地质相关岗位招聘汇总

3s等相关专业25秋招&提前批招聘信息 该岗位信息表&#xff0c;覆盖全国各大省市&#xff0c;招聘岗位主要针对地信、测绘、地质、遥感、城规等专业。 1800WebGIS开发岗位汇总表 该信息表&#xff0c;主要是WebGIS开发岗为主&#xff0c;岗位要求熟悉熟悉Openlayers&#…

【C++篇】~类和对象(中)

类和对象&#xff08;中&#xff09; 1.类的默认成员函数​ 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0c;需要注意的是这6个中最重要的是前…

YOLOv8改进系列,YOLOv8替换主干网络为PP-HGNetV2(百度飞桨视觉团队自研,助力涨点)

摘要 PP-HGNetV2(High Performance GPU Network V2) 是百度飞桨视觉团队自研的 PP-HGNet 的下一代版本,其在 PP-HGNet 的基础上,做了进一步优化和改进,最终在 NVIDIA GPU 设备上,将 “Accuracy-Latency Balance” 做到了极致,精度大幅超过了其他同样推理速度的模型。其在…

远程桌面连接工具Microsoft Remote Desktop Beta for Mac

Microsoft Remote Desktop Beta for Mac 是一款功能强大的远程桌面连接工具&#xff0c;具有以下功能特点&#xff1a; 软件下载地址 跨平台连接&#xff1a; 允许 Mac 用户轻松连接到运行 Windows 操作系统的计算机&#xff0c;打破了操作系统的界限&#xff0c;无论这些 Wi…

什么是HTTP DDOS,如何防护

在当今高度互联的网络世界中&#xff0c;网络安全威胁日益严峻&#xff0c;其中HTTP DDoS&#xff08;Distributed Denial of Service&#xff0c;分布式拒绝服务&#xff09;攻击作为一种常见的网络攻击手段&#xff0c;给企业和个人用户带来了巨大的挑战。今天我们就来详细介…