Python中两个最常用的单元测试框架unittest和pytest

news/2024/12/22 20:04:06/

这篇文章的主要目的是讨论Python中两个最常用的单元测试框架unittest和pytest,它们的优缺点以及在何时选择哪个框架。


Python unittest vs Pytest

在编写任何软件时,我们必须在开发过程中保持错误检查的过程。这确保了一旦软件达到发布阶段,在使用过程中遇到的错误数量最小。

Python也有各种测试框架,可以通过给定不同的输入来测试编写的代码,以检查其行为。

如果遇到任何错误,可以在开发阶段进行修正,而不是在应用程序初始发布之后进行紧急修补。

示例代码:

class Calculate:def CheckPrime(self, a):for i in range(a):if (a % i):return Falsereturn Truedef CalcFact(self, a):if (a == 1):return aelse:return a * self.fact(a-1)

上面的代码包含了两个函数CheckPrime和CalcFact,根据它们的名称可以看出它们检查素数和计算阶乘。

为了确保Calculate方法正常工作,必须检查通过给定不同输出产生的可能出现的错误。

那么,我们如何做到这一点?为了确保我们的代码没有错误,我们可以使用不同的测试框架编写测试用例,并在其上测试我们的代码以检查代码的完整性。

虽然有许多测试框架,但最常用的两个是unittest和pytest。让我们逐个探讨它们。

unittest框架的单元测试

unittest是Python标准库中包含的单元测试框架。该框架受到了JUnit的启发,JUnit是一个用于单元测试的Java框架。

在讨论unittest的工作原理之前,了解unittest中常用的术语是很重要的(也用于其他相关框架)。

  • 测试用例(Test Case)- 最小的测试单位- 通常包含一个或多个
  • 测试套件(Test Suite)- 组合的测试用例- 通常依次执行
  • 测试运行器(Test Runner)- 协调和处理测试用例和测试套件的执行

由于Python的标准库已经包含unittest,因此无需下载任何外部模块即可开始使用unittest编写单元测试。

我们可以在导入unittest模块后开始。现在,让我们专注于之前的代码。

示例代码:

import unittestclass Calculate:def CheckPrime(self, a):for i in range(2, a):if (a % i == 0):return Falsereturn Truedef CalcFact(self, a):if (a == 1):return aelse:return a * self.CalcFact(a-1)class TestCalc(unittest.TestCase):def test_CheckPrime(self):calc = Calculate()# Passing different outputsself.assertEqual(calc.CheckPrime(2), True)self.assertEqual(calc.CheckPrime(3), True)self.assertEqual(calc.CheckPrime(4), False)self.assertEqual(calc.CheckPrime(80), False)def test_CheckFact(self):calc = Calculate()# Passing different outputsself.assertEqual(calc.CalcFact(2), 2)self.assertEqual(calc.CalcFact(3), 6)

输出:

PS D:\Unittest> python -m unittest a.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000sOK

从输出可以看出,所有的测试用例都通过了,因为所有的断言都成功了。

现在让我们尝试一个测试用例失败的情况。

def test_CheckFact(self):calc = Calculate()# Passing different outputsself.assertEqual(calc.CalcFact(2), 2)self.assertEqual(calc.CalcFact(3), 6)# Supposed to throw an errorself.assertEqual(calc.CalcFact(0), 0)

输出:

PS D:\Unittest> python -m unittest a.py
======================================================================
ERROR: test_CheckFact (a.TestCalc)
----------------------------------------------------------------------
Traceback (most recent call last):File "D:\Python Articles\a.py", line 34, in test_CheckFactself.assertEqual(calc.CalcFact(0), 0) # Supposed to throw an errorFile "D:\Python Articles\a.py", line 15, in CalcFactreturn a * self.CalcFact(a-1)File "D:\Python Articles\a.py", line 15, in CalcFactreturn a * self.CalcFact(a-1)File "D:\Python Articles\a.py", line 15, in CalcFactreturn a * self.CalcFact(a-1)[The previous line is repeated 974 more times]File "D:\Python Articles\a.py", line 12, in CalcFactif (a == 1):
RecursionError: maximum recursion depth exceeded in comparison----------------------------------------------------------------------
Ran 2 tests in 0.004s
FAILED (errors=1)

从代码中可以看出,我们使用python -m unittest <name_of_script.py>来执行脚本。

这段代码在未调用测试类的方法的情况下工作,因为unittest模块以特定的格式处理给定的脚本文件。

由于我们的脚本包含TestCalc,unittest.TestCase的子类会被自动实例化。

在实例化之后,会找到类内部的测试方法,并按顺序执行。要将方法视为测试方法,它必须以test_开头。

找到测试方法后,它们按顺序调用;在我们的情况下,调用了test_CheckPrime和test_CheckFact两个方法。在我们的实现中,检查断言,并在出现意外行为时将错误抛出到输出中。

从包含错误的测试用例中可以推断出,由于代码的编写方式,CalcFact方法中开始发生了无限递归,现在可以通过测试用例来修复它。

如果你想知道为什么会发生错误,那是因为初始条件没有检查小于1的数字。

以下是使用unittest的一些优点:

  • 包含在Python标准库中
  • 将相关的测试用例放入单个测试套件中
  • 测试集合速度快
  • 精确的测试时间持续时间

unittest具有以下缺点:

  • 可能难以理解
  • 没有彩色输出
  • 可能太冗长

Pytest框架的单元测试

与unittest不同,Pytest不是一个内置模块,我们必须单独下载它。不过,安装Pytest相对简单;我们可以使用pip来执行以下命令:

pip install pytest

让我们使用Pytest编写一些测试用例。在开始之前,让我们看一下Pytest与unittest在编写测试用例方面的区别。对于使用Pytest编写的单元测试,我们必须:

  • 创建一个单独的目录,并将要测试的脚本放在新创建的目录中。
  • 在文件中编写测试,文件要么以test_开头,要么以_test.py结尾。例如,test_calc.py或calc_test.py。

考虑以下使用Pytest编写的代码。

def test_CheckPrime():calc = Calculate()# Passing different outputsassert calc.CheckPrime(2) == Trueassert calc.CheckPrime(3) == Trueassert calc.CheckPrime(4) == Falseassert calc.CheckPrime(80) == Falsedef test_CheckFact():calc = Calculate()# Passing different outputsassert calc.CalcFact(2) == 2assert calc.CalcFact(3) == 6# assert calc.CalcFact(0) == 0 # Supposed to throw an error

输出:

============================================================== test session starts ==============================================================
platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\Unittest
collected 2 itemstest_a.py
[100%]=============================================================== 2 passed in 0.04s ===============================================================

现在,让我们来看一个失败的测试用例。

============================================================== test session starts ==============================================================
platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\Unittest
collected 2 itemstest_a.py .F
[100%]=================================================================== FAILURES ====================================================================
________________________________________________________________ test_CheckFact _________________________________________________________________def test_CheckFact():calc = Calculate()# Passing different outputsassert calc.CalcFact(2) == 2assert calc.CalcFact(3) == 6
>       assert calc.CalcFact(0) == 0 # Supposed to throw an errortest_a.py:50: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test_a.py:13: in CalcFactreturn a * self.CalcFact(a-1)
test_a.py:13: in CalcFactreturn a * self.CalcFact(a-1)
test_a.py:13: in CalcFactreturn a * self.CalcFact(a-1)
.
.
.
.
.
RecursionError: maximum recursion depth exceeded in comparisontest_a.py:10: RecursionError
============================================================ short test summary info ============================================================ 
FAILED test_a.py::test_CheckFact - RecursionError: maximum recursion depth exceeded in comparison
========================================================== 1 failed, 1 passed in 2.42s ========================================================== 

使用 Pytest 编写的测试用例比 unittest 简单一点; 不用创建一个作为 unittest.TestCase 子类的类,我们可以在方法的开头使用 test_ 编写我们的测试函数。

以下是在 Python 中使用 Pytest 框架的一些优点。

  • 紧凑的测试套件
  • 最小样板代码
  • 插件支持
  • 整洁和适当的输出呈现
  • 通常与其他框架不兼容

推荐Python自动化测试学习视频

软件测试黄胖子
软件测试面试官黄财财


http://www.ppmy.cn/news/1124781.html

相关文章

打印大对象日志导致GC问题的解决

内容&#xff1a; rpc调用外部服务时&#xff0c;需要将req和resp的信息打印出来&#xff0c;以便于排查问题。但是有的rpc服务的resp信息过于庞大&#xff0c;比如resp中有List<>信息&#xff0c;list很大很大时会导致log.info打印信息时&#xff0c;产生GC&#xff0c…

爬虫异常处理实战:应对请求频率限制和数据格式异常

作为一名资深的爬虫程序员&#xff0c;今天我要和大家分享一些实战经验&#xff0c;教你如何处理爬虫中的异常情况&#xff0c;包括请求频率限制和数据格式异常。如果你是一个正在进行网络爬虫开发的开发者&#xff0c;或者对异常处理感兴趣&#xff0c;那么这篇文章将帮助你更…

DAZ To UMA⭐三.导入Blender的配置, 及Blender快捷键

文章目录 🟥 Blender快捷键1️⃣ 3D视图快捷键2️⃣ 视角快捷键3️⃣ 编辑快捷键4️⃣ 对物体的操作🟧 Blender导入FBX的配置🟩 设置脸部骨骼大小1️⃣ 切换视角2️⃣ 缩小脸部骨骼3️⃣ 本节效果预览🟦 设置眼角膜透明度🟥 Blender快捷键 1️⃣ 3D视图快捷键 快捷键…

Go语言中的自定义函数类型

函数类型的基本概念 在Go语言中&#xff0c;函数类型是一种将函数作为值的数据类型。与其他类型一样&#xff0c;函数类型可以被声明、赋值给变量&#xff0c;作为参数传递和作为返回值返回。通过函数类型&#xff0c;我们可以将函数看作一种可执行的对象&#xff0c;对其进行…

HTML计时事件(JavaScript)网页电子钟+网页计时器

setTimeout("函数","未来指定毫秒后调用函数"); clearTimeout(setTimeout("函数","未来指定毫秒后调用函数")); <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title>…

常用工具使用

ubuntu 1.1 ubuntu与windows 互相复制问题 方法一、 打开虚拟机 &#xff0c;点击上方导航栏 ‘虚拟机’ 查看VMware Tools是否安装&#xff0c;安装即可 方法二、 apt-get autoremove open-vm-tools apt-get install open-vm-tools apt-get install open-vm-tools-desktop…

使用 PyTorch 的计算机视觉简介 (1/6)

一、说明 Computer Vision&#xff08;CV&#xff09;是一个研究计算机如何从数字图像和/或视频中获得一定程度的理解的领域。理解这个定义具有相当广泛的含义 - 它可以从能够区分图片上的猫和狗&#xff0c;到更复杂的任务&#xff0c;例如用自然语言描述图像。 二、CV常见的问…

Tomcat 开启远程调试

Tomcat 部署的 war包工程开启远程调试 Linux服务器下&#xff0c;编辑Tomcat bin 目录下的 startup.sh 文件 vim startup.sh在第一行加入&#xff1a;(不换行&#xff0c;在同一行) declare -x CATALINA_OPTS"-server -Xdebug -Xnoagent -Djava.compilerNONE -Xrunjdwp:…