单元测试框架unittest和pytest

Python unittest vs Pytest

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

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

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

示例代码:

  1. class Calculate:

  2. def CheckPrime(self, a):

  3. for i in range(a):

  4. if (a % i):

  5. return False

  6. return True

  7. def CalcFact(self, a):

  8. if (a == 1):

  9. return a

  10. else:

  11. 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模块后开始。现在,让我们专注于之前的代码。

示例代码:

  1. import unittest

  2. class Calculate:

  3. def CheckPrime(self, a):

  4. for i in range(2, a):

  5. if (a % i == 0):

  6. return False

  7. return True

  8. def CalcFact(self, a):

  9. if (a == 1):

  10. return a

  11. else:

  12. return a * self.CalcFact(a-1)

  13. class TestCalc(unittest.TestCase):

  14. def test_CheckPrime(self):

  15. calc = Calculate()

  16. # Passing different outputs

  17. self.assertEqual(calc.CheckPrime(2), True)

  18. self.assertEqual(calc.CheckPrime(3), True)

  19. self.assertEqual(calc.CheckPrime(4), False)

  20. self.assertEqual(calc.CheckPrime(80), False)

  21. def test_CheckFact(self):

  22. calc = Calculate()

  23. # Passing different outputs

  24. self.assertEqual(calc.CalcFact(2), 2)

  25. self.assertEqual(calc.CalcFact(3), 6)

输出:

  1. PS D:\Unittest> python -m unittest a.py

  2. ..

  3. ----------------------------------------------------------------------

  4. Ran 2 tests in 0.000s

  5. OK

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

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

  1. def test_CheckFact(self):

  2. calc = Calculate()

  3. # Passing different outputs

  4. self.assertEqual(calc.CalcFact(2), 2)

  5. self.assertEqual(calc.CalcFact(3), 6)

  6. # Supposed to throw an error

  7. self.assertEqual(calc.CalcFact(0), 0)

输出:

  1. PS D:\Unittest> python -m unittest a.py

  2. ======================================================================

  3. ERROR: test_CheckFact (a.TestCalc)

  4. ----------------------------------------------------------------------

  5. Traceback (most recent call last):

  6. File "D:\Python Articles\a.py", line 34, in test_CheckFact

  7. self.assertEqual(calc.CalcFact(0), 0) # Supposed to throw an error

  8. File "D:\Python Articles\a.py", line 15, in CalcFact

  9. return a * self.CalcFact(a-1)

  10. File "D:\Python Articles\a.py", line 15, in CalcFact

  11. return a * self.CalcFact(a-1)

  12. File "D:\Python Articles\a.py", line 15, in CalcFact

  13. return a * self.CalcFact(a-1)

  14. [The previous line is repeated 974 more times]

  15. File "D:\Python Articles\a.py", line 12, in CalcFact

  16. if (a == 1):

  17. RecursionError: maximum recursion depth exceeded in comparison

  18. ----------------------------------------------------------------------

  19. Ran 2 tests in 0.004s

  20. 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编写的代码。

  1. def test_CheckPrime():

  2. calc = Calculate()

  3. # Passing different outputs

  4. assert calc.CheckPrime(2) == True

  5. assert calc.CheckPrime(3) == True

  6. assert calc.CheckPrime(4) == False

  7. assert calc.CheckPrime(80) == False

  8. def test_CheckFact():

  9. calc = Calculate()

  10. # Passing different outputs

  11. assert calc.CalcFact(2) == 2

  12. assert calc.CalcFact(3) == 6

  13. # assert calc.CalcFact(0) == 0 # Supposed to throw an error

输出:

  1. ============================================================== test session starts ==============================================================

  2. platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0

  3. rootdir: D:\Unittest

  4. collected 2 items

  5. test_a.py

  6. [100%]

  7. =============================================================== 2 passed in 0.04s ===============================================================

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

  1. ============================================================== test session starts ==============================================================

  2. platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0

  3. rootdir: D:\Unittest

  4. collected 2 items

  5. test_a.py .F

  6. [100%]

  7. =================================================================== FAILURES ====================================================================

  8. ________________________________________________________________ test_CheckFact _________________________________________________________________

  9. def test_CheckFact():

  10. calc = Calculate()

  11. # Passing different outputs

  12. assert calc.CalcFact(2) == 2

  13. assert calc.CalcFact(3) == 6

  14. > assert calc.CalcFact(0) == 0 # Supposed to throw an error

  15. test_a.py:50:

  16. _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

  17. test_a.py:13: in CalcFact

  18. return a * self.CalcFact(a-1)

  19. test_a.py:13: in CalcFact

  20. return a * self.CalcFact(a-1)

  21. test_a.py:13: in CalcFact

  22. return a * self.CalcFact(a-1)

  23. .

  24. .

  25. .

  26. .

  27. .

  28. RecursionError: maximum recursion depth exceeded in comparison

  29. test_a.py:10: RecursionError

  30. ============================================================ short test summary info ============================================================

  31. FAILED test_a.py::test_CheckFact - RecursionError: maximum recursion depth exceeded in comparison

  32. ========================================================== 1 failed, 1 passed in 2.42s ==========================================================

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

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

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

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值