Python为开发者提供了内置的单元测试框架 unittest,它是一种强大的工具,能够有效地编写和执行单元测试unittest 提供了完整的测试结构,支持自动化测试的执行,能够对测试用例进行组织,并且提供了丰富的断言方法。最终,unittest 会生成详细的测试报告,这个框架非常简单且易于使用。

unittest核心概念

在 unittest 中,有四个核心概念:

  1. TestCase(测试用例):每个测试用例实例用于封装一个或多个测试函数。
  2. TestSuite(测试套件):这是多个测试用例的集合,用于组织和执行多个测试用例。
  3. TestLoader(测试加载器):这是一个用于将测试用例加载到测试套件中的工具。
  4. TextTestRunner(测试运行器):这是用于执行测试用例的运行器,负责运行测试并生成结果报告。
  5. Fixture(环境管理机制):这是测试用例的环境搭建和销毁部分,包括前置条件和后置条件。

unittest的工作流程
  1. 编写继承自 unittest.TestCase 的测试用例类,其中每个测试函数都是一个独立的测试用例。
  2. 使用 TestLoader 加载测试用例,并将它们组织成 TestSuite 对象。
  3. 使用 TestRunner 运行 TestSuite 中的测试用例,并输出测试结果。

使用unittest初级指南

  1. 导入 unittest 模块以及被测试的文件或类。
  2. 创建一个测试类,并继承 unittest.TestCase,所有自定义的单元测试类都要继承它,作为基类。
  3. 重写 setUp 和 tearDown 方法,用于初始化和清理测试环境(如果有必要)。
  4. 定义测试函数,函数名以 test_ 开头,这样才能被识别并执行。
  5. 在测试函数中使用断言来判断测试结果是否符合预期。
  6. 调用 unittest.main() 方法运行测试用例,按照函数名的排序执行测试。

以下是一个简单的例子:


  1. import unittest

  2. def login(username, password):

  3. if username == 'kira' and password == '123':

  4. res = {"code": 200, "msg": "登录成功"}

  5. return res

  6. return {"code": 400, "msg": "登录失败"}

  7. class TestLogin(unittest.TestCase):

  8. def test_login_success(self):

  9. """测试登录成功"""

  10. test_data = {"username": "kira", "password": "test"}

  11. expect_data = {"code": 200, "msg": "登录成功"}

  12. res = login(**test_data)

  13. self.assertEqual(res, expect_data)

  14. def test_login_error_with_error_password(self):

  15. """账号正确,密码错误,登录失败"""

  16. test_data = {"username": "kira", "password": "12345"}

  17. expect_data = {"code": 400, "msg": "登录失败"}

  18. res = login(**test_data)

  19. self.assertEqual(res, expect_data)

  20. # 更多测试函数类似...

  21. if __name__ == '__main__':

  22. unittest.main()

AI写代码

以上是一个简单的测试用例,包含了两个测试函数。运行脚本将输出测试结果。

unittest核心概念

测试脚手架

测试脚手架 是测试用例的前置条件和后置条件,确保测试环境的初始化和清理,从而保证测试的准确性和可靠性。


  1. import unittest

  2. class MyTestCase(unittest.TestCase):

  3. @classmethod

  4. def setUpClass(cls):

  5. # 类级别的前置条件设置,整个类运行最先只执行一次

  6. print("setUpClass")

  7. @classmethod

  8. def tearDownClass(cls):

  9. # 类级别的后置条件清理,整个类运行最后结束执行一次

  10. print("tearDownClass")

  11. def setUp(self):

  12. # 测试方法级别的前置条件设置,所有测试方法运行前都执行一次

  13. print("setUp")

  14. def tearDown(self):

  15. # 测试方法级别的后置条件清理,所有测试方法运行结束都执行一次

  16. print("tearDown")

  17. def test_example(self):

  18. # 测试用例

  19. print("test_example")

  20. if __name__ == "__main__":

  21. unittest.main()

AI写代码

  1. setUp():每个测试方法运行前执行,用于测试前置的初始化工作。
  2. tearDown():每个测试方法结束后执行,用于测试后的清理工作。
  3. setUpClass():所有的测试方法运行前执行,用于单元测试类运行前的准备工作。使用 @classmethod 装饰器装饰,整个测试类运行过程中只会执行一次。
  4. tearDownClass():所有的测试方法结束后执行,用于单元测试类运行后的清理工作。使用 @classmethod 装饰器装饰,整个测试类运行过程中只会执行一次。
测试用例

测试用例 是最小的测试单元,用于检测特定的输入集合的特定的返回值。unittest 提供了 TestCase 基类,所有的测试类都需要继承该基类,而在该类下的函数如果以 test_ 开头,则被标识为测试函数:


  1. class MyTestCase(unittest.TestCase

  2. ):

  3. def test_addition(self):

  4. result = 2 + 3

  5. self.assertEqual(result, 5) # 使用断言方法验证结果是否相等

  6. def test_subtraction(self):

  7. result = 5 - 3

  8. self.assertTrue(result == 2) # 使用断言方法验证结果是否为True

  9. # 更多测试用例函数...

AI写代码

断言方法

以下是常用的断言方法:

  • assertEqual(a, b, msg=None):验证 a 等于 b。
  • assertNotEqual(a, b):验证 a 不等于 b。
  • assertTrue(x):验证 x 是否为 True。
  • assertFalse(x):验证 x 是否为 False。
  • assertIs(a, b):验证 a 是否是 b。
  • assertIsNot(a, b):验证 a 是否不是 b。
  • assertIsNone(x):验证 x 是否为 None。
  • assertIsNotNone(x):验证 x 是否不为 None。
  • assertIn(a, b):验证 a 是否在 b 中。
  • assertNotIn(a, b):验证 a 是否不在 b 中。
  • assertIsInstance(a, b):验证 a 是否是 b 类型的实例。
  • assertNotIsInstance(a, b):验证 a 是否不是 b 类型的实例。

可以使用这些方法进行断言,也可以直接使用原生的assert来断言,如果断言失败,测试用例会被定义为执行失败。

忽略特定测试方法

unittest 提供了一些方法来跳过特定的测试用例:

  • @unittest.skip(reason):强制跳过,reason 是跳过的原因。
  • @unittest.skipIf(condition, reason):当 condition 为 True 时跳过。
  • @unittest.skipUnless(condition, reason):当 condition 为 False 时跳过。
  • @unittest.expectedFailure:如果测试失败,这个测试用例不会计入失败的统计。
  • 使用实例方法:self.skipTest() 使用和上述类似。

  1. import sys

  2. import unittest

  3. class Test1(unittest.TestCase):

  4. @unittest.expectedFailure # 即使失败也会被计为成功的用例

  5. def test_1(self):

  6. assert 1 + 1 == 3

  7. @unittest.skip('无条件跳过') # 不管什么情况都会进行跳过

  8. def test_2(self):

  9. print("2+2...", 4)

  10. @unittest.skipIf(sys.platform == "win32", "跳过") # 如果系统平台为 Windows 则跳过

  11. def test_3(self):

  12. print("3+3...", 6)

  13. @unittest.skipUnless(sys.platform == "win32", "跳过") # 除非系统平台为 Windows,否则跳过

  14. def test_4(self):

  15. print("4+4...", 8)

  16. def test_5(self):

  17. self.skipTest("跳过")

  18. print("5+5...", 10)

  19. if __name__ == "__main__":

  20. unittest.main(verbosity=2)

AI写代码

测试套件

测试套件用于收集和组织多个测试用例,便于集中执行。

  1. 通过 unittest.main() 方法直接加载单元测试的测试模块,这是一种简单的加载方式。所有测试用例的执行顺序按照方法名的字符串表示的 ASCII 码升序排序,通过命名时使用 test_01_xxx 来指定执行顺序。
  2. 将所有的单元测试用例 TestCase 加载到测试套件 Test Suite 集合中,然后一次性加载所有测试对象。
通过 TestSuite 对象收集

此方式适用于需要自定义组合特定测试用例的情况。


  1. import unittest

  2. class MyTestCase(unittest.TestCase):

  3. def test_addition(self):

  4. result = 2 + 3

  5. self.assertEqual(result, 5)

  6. def suite():

  7. suite = unittest.TestSuite()

  8. suite.addTest(MyTestCase('test_addition'))

  9. return suite

  10. if __name__ == '__main__':

  11. runner = unittest.TextTestRunner()

  12. runner.run(suite())

AI写代码

通过 TestLoader 对象收集

TestLoader 是 unittest 框架提供的加载测试用例的类。


  1. import unittest

  2. if __name__ == '__main__':

  3. loader = unittest.defaultTestLoader

  4. # 自动加载当前模块中所有以 'test_' 开头的测试用例函数

  5. suite = loader.loadTestsFromModule(__name__)

  6. runner = unittest.TextTestRunner()

  7. runner.run(suite)

  8. import unittest

  9. class MyTestCase(unittest.TestCase):

  10. def test_addition(self):

  11. result = 2 + 3

  12. self.assertEqual(result, 5)

  13. if __name__ == '__main__':

  14. loader = unittest.defaultTestLoader

  15. # 自动加载 MyTestCase 类中的所有测试用例

  16. suite = loader.loadTestsFromTestCase(MyTestCase)

  17. runner = unittest.TextTestRunner()

  18. runner.run(suite)

  19. import unittest

  20. if __name__ == '__main__':

  21. loader = unittest.defaultTestLoader

  22. # 自动加载指定名称的测试用例

  23. suite = loader.loadTestsFromName('module.MyTestCase.test_addition')

  24. runner = unittest.TextTestRunner()

  25. runner.run(suite)

  26. import unittest

  27. if __name__ == '__main__':

  28. loader = unittest.defaultTestLoader

  29. # 自动发现并加载指定目录中的测试用例模块

  30. suite = loader.discover(start_dir='test_directory', pattern='test_*.py', top_level_dir=None)

  31. runner = unittest.TextTestRunner()

  32. runner.run(suite)

AI写代码

测试运行器

测试运行器是用于执行和输出测试结果的组件。常用的运行器有:

  • unittest.TextTestRunner:这是 unittest 框架中默认的测试运行器,会在命令行输出测试结果。通过调用 run() 方法运行测试套件,并将测试结果打印到控制台。

  1. import unittest

  2. if __name__ == '__main__':

  3. loader = unittest.defaultTestLoader

  4. suite = loader.discover(start_dir='tests', pattern='test_*.py')

  5. runner = unittest.TextTestRunner()

  6. result = runner.run(suite)

AI写代码

  • HTMLTestRunner:这是一个第三方库,能够生成漂亮的 HTML 测试报告,需要进行安装。你可以通过搜索获取相关文件进行安装。

  1. import unittest

  2. from HTMLTestRunner import HTMLTestRunner

  3. if __name__ == '__main__':

  4. loader = unittest.defaultTestLoader

  5. suite = loader.discover(start_dir='tests', pattern='test_*.py')

  6. with open('test_report.html', 'wb') as report_file:

  7. runner = HTMLTestRunner(stream=report_file, title='Test Report', description='Test Results')

  8. result = runner.run(suite)

AI写代码

  • XMLTestRunner:这是另一个第三方库,用于生成 XML 格式的测试报告。

  1. import unittest

  2. from xmlrunner import XMLTestRunner

  3. if __name__ == '__main__':

  4. loader = unittest.defaultTestLoader

  5. suite = loader.discover(start_dir='tests', pattern='test_*.py')

  6. with open('test_report.xml', 'wb') as report_file:

  7. runner = XMLTestRunner(output=report_file)

  8. result = runner.run(suite)

AI写代码

你也可以自定义测试运行器。继承 unittest.TestRunner 类并实现 run() 方法,以创建自己的测试运行器。


  1. import unittest

  2. class MyTestRunner(unittest.TextTestRunner):

  3. def run(self, test):

  4. print("Running tests with MyTestRunner")

  5. result = super().run(test)

  6. return result

  7. if __name__ == '__main__':

  8. loader = unittest.defaultTestLoader

  9. suite = loader.discover(start_dir='tests', pattern='test_*.py')

  10. runner = MyTestRunner()

  11. result = runner.run(suite)

AI写代码

通常使用 HTMLTestRunner 即可满足需求,它非常易用。

实战一个测试案例

假设有一个测试函数 login


  1. # login.py

  2. def login(username, password):

  3. """模拟登录校验"""

  4. if username == 'kira' and password == '123456':

  5. return {"code": 0, "msg": "登录成功"}

  6. else:

  7. return {"code": 1, "msg": "账号或密码不正确"}

AI写代码

设计用例

根据函数的参数和逻辑,设计如下用例:

序号 标题 测试数据 预期结果 实际结果
1 账号密码正确 {"username": "kira", "password": "123456"} {"code": 0, "msg": "登录成功"}
2 账号正确密码不正确 {"username": "kira", "password": "123"} {"code": 1, "msg": "账号或密码不正确"}
3 账号错误密码正确 {"username": "kir", "password": "123456"} {"code": 1, "msg": "账号或密码不正确"}
编写测试用例并运行

  1. import unittest

  2. from login import login

  3. class TestLogin(unittest.TestCase):

  4. def test_login_correct(self):

  5. """测试账号密码正确"""

  6. test_data = {"username": "kira", "password": "123456"}

  7. expect_data = {"code": 0, "msg": "登录成功"}

  8. res = login(**test_data)

  9. self.assertEqual(res, expect_data)

  10. def test_login_wrong_password(self):

  11. """测试账号正确密码不正确"""

  12. test_data = {"username": "kira", "password": "123"}

  13. expect_data = {"code": 1, "msg": "账号或密码不正确"}

  14. res = login(**test_data)

  15. self.assertEqual(res, expect_data)

  16. def test_login_wrong_username(self):

  17. """测试账号错误密码正确"""

  18. test_data = {"username": "kir", "password": "123456"}

  19. expect_data = {"code": 1, "msg": "账号或密码不正确"}

  20. res = login(**test_data)

  21. self.assertEqual(res, expect_data)

  22. if __name__ == '__main__':

  23. unittest.main()

AI写代码python运行

这是一个简单的测试用例,包含了三个测试函数。运行测试用例后,会输出测试结果,看完是否觉得unittest非常简单易用。ner.run(suite)

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

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

软件测试面试文档

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

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

Logo

惟楚有才,于斯为盛。欢迎来到长沙!!! 茶颜悦色、臭豆腐、CSDN和你一个都不能少~

更多推荐