当前位置:首页 > 科技  > 软件

不拼花哨,只拼实用:Unittest指南,干货为王!

来源: 责编: 时间:2023-09-18 21:40:46 437观看
导读Python为开发者提供了内置的单元测试框架 unittest,它是一种强大的工具,能够有效地编写和执行单元测试。unittest 提供了完整的测试结构,支持自动化测试的执行,能够对测试用例进行组织,并且提供了丰富的断言方法。最终,unit

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

QgV28资讯网——每日最新资讯28at.com

unittest的工作流程

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

QgV28资讯网——每日最新资讯28at.com

使用unittest初级指南

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

以下是一个简单的例子:QgV28资讯网——每日最新资讯28at.com

import unittestdef login(username, password):    if username == 'kira' and password == '123':        res = {"code": 200, "msg": "登录成功"}        return res    return {"code": 400, "msg": "登录失败"}class TestLogin(unittest.TestCase):    def test_login_success(self):        """测试登录成功"""        test_data = {"username": "kira", "password": "test"}        expect_data = {"code": 200, "msg": "登录成功"}        res = login(**test_data)        self.assertEqual(res, expect_data)    def test_login_error_with_error_password(self):        """账号正确,密码错误,登录失败"""        test_data = {"username": "kira", "password": "12345"}        expect_data = {"code": 400, "msg": "登录失败"}        res = login(**test_data)        self.assertEqual(res, expect_data)    # 更多测试函数类似...if __name__ == '__main__':    unittest.main()

以上是一个简单的测试用例,包含了两个测试函数。运行脚本将输出测试结果。QgV28资讯网——每日最新资讯28at.com

unittest核心概念

测试脚手架

测试脚手架 是测试用例的前置条件和后置条件,确保测试环境的初始化和清理,从而保证测试的准确性和可靠性。QgV28资讯网——每日最新资讯28at.com

import unittestclass MyTestCase(unittest.TestCase):    @classmethod    def setUpClass(cls):        # 类级别的前置条件设置,整个类运行最先只执行一次        print("setUpClass")    @classmethod    def tearDownClass(cls):        # 类级别的后置条件清理,整个类运行最后结束执行一次        print("tearDownClass")    def setUp(self):        # 测试方法级别的前置条件设置,所有测试方法运行前都执行一次        print("setUp")    def tearDown(self):        # 测试方法级别的后置条件清理,所有测试方法运行结束都执行一次        print("tearDown")    def test_example(self):        # 测试用例        print("test_example")if __name__ == "__main__":    unittest.main()
  • setUp():每个测试方法运行前执行,用于测试前置的初始化工作。
  • tearDown():每个测试方法结束后执行,用于测试后的清理工作。
  • setUpClass():所有的测试方法运行前执行,用于单元测试类运行前的准备工作。使用 @classmethod 装饰器装饰,整个测试类运行过程中只会执行一次。
  • tearDownClass():所有的测试方法结束后执行,用于单元测试类运行后的清理工作。使用 @classmethod 装饰器装饰,整个测试类运行过程中只会执行一次。

测试用例

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

class MyTestCase(unittest.TestCase):    def test_addition(self):        result = 2 + 3        self.assertEqual(result, 5)  # 使用断言方法验证结果是否相等    def test_subtraction(self):        result = 5 - 3        self.assertTrue(result == 2)  # 使用断言方法验证结果是否为True    # 更多测试用例函数...

断言方法

以下是常用的断言方法:QgV28资讯网——每日最新资讯28at.com

  • 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来断言,如果断言失败,测试用例会被定义为执行失败。QgV28资讯网——每日最新资讯28at.com

忽略特定测试方法

unittest 提供了一些方法来跳过特定的测试用例:QgV28资讯网——每日最新资讯28at.com

  • @unittest.skip(reason):强制跳过,reason 是跳过的原因。
  • @unittest.skipIf(condition, reason):当 condition 为 True 时跳过。
  • @unittest.skipUnless(condition, reason):当 condition 为 False 时跳过。
  • @unittest.expectedFailure:如果测试失败,这个测试用例不会计入失败的统计。

使用实例方法:self.skipTest() 使用和上述类似。QgV28资讯网——每日最新资讯28at.com

import sysimport unittestclass Test1(unittest.TestCase):    @unittest.expectedFailure  # 即使失败也会被计为成功的用例    def test_1(self):        assert 1 + 1 == 3    @unittest.skip('无条件跳过')  # 不管什么情况都会进行跳过    def test_2(self):        print("2+2...", 4)    @unittest.skipIf(sys.platform == "win32", "跳过")  # 如果系统平台为 Windows 则跳过    def test_3(self):        print("3+3...", 6)    @unittest.skipUnless(sys.platform == "win32", "跳过")  # 除非系统平台为 Windows,否则跳过    def test_4(self):        print("4+4...", 8)    def test_5(self):        self.skipTest("跳过")        print("5+5...", 10)if __name__ == "__main__":    unittest.main(verbosity=2)

测试套件

测试套件用于收集和组织多个测试用例,便于集中执行。QgV28资讯网——每日最新资讯28at.com

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

通过 TestSuite 对象收集

此方式适用于需要自定义组合特定测试用例的情况。QgV28资讯网——每日最新资讯28at.com

import unittestclass MyTestCase(unittest.TestCase):    def test_addition(self):        result = 2 + 3        self.assertEqual(result, 5)def suite():    suite = unittest.TestSuite()    suite.addTest(MyTestCase('test_addition'))    return suiteif __name__ == '__main__':    runner = unittest.TextTestRunner()    runner.run(suite())

通过 TestLoader 对象收集

TestLoader 是 unittest 框架提供的加载测试用例的类。QgV28资讯网——每日最新资讯28at.com

import unittestif __name__ == '__main__':    loader = unittest.defaultTestLoader        # 自动加载当前模块中所有以 'test_' 开头的测试用例函数    suite = loader.loadTestsFromModule(__name__)    runner = unittest.TextTestRunner()    runner.run(suite)
import unittestclass MyTestCase(unittest.TestCase):    def test_addition(self):        result = 2 + 3        self.assertEqual(result, 5)if __name__ == '__main__':    loader = unittest.defaultTestLoader        # 自动加载 MyTestCase 类中的所有测试用例    suite = loader.loadTestsFromTestCase(MyTestCase)    runner = unittest.TextTestRunner()    runner.run(suite)
import unittestif __name__ == '__main__':    loader = unittest.defaultTestLoader        # 自动加载指定名称的测试用例    suite = loader.loadTestsFromName('module.MyTestCase.test_addition')    runner = unittest.TextTestRunner()    runner.run(suite)
import unittestif __name__ == '__main__':    loader = unittest.defaultTestLoader        # 自动发现并加载指定目录中的测试用例模块    suite = loader.discover(start_dir='test_directory', pattern='test_*.py', top_level_dir=None)    runner = unittest.TextTestRunner()    runner.run(suite)

测试运行器

测试运行器是用于执行和输出测试结果的组件。常用的运行器有:QgV28资讯网——每日最新资讯28at.com

  • unittest.TextTestRunner:这是 unittest 框架中默认的测试运行器,会在命令行输出测试结果。通过调用 run() 方法运行测试套件,并将测试结果打印到控制台。
import unittestif __name__ == '__main__':    loader = unittest.defaultTestLoader    suite = loader.discover(start_dir='tests', pattern='test_*.py')        runner = unittest.TextTestRunner()    result = runner.run(suite)
  • HTMLTestRunner:这是一个第三方库,能够生成漂亮的 HTML 测试报告,需要进行安装。你可以通过搜索获取相关文件进行安装。
import unittestfrom HTMLTestRunner import HTMLTestRunnerif __name__ == '__main__':    loader = unittest.defaultTestLoader    suite = loader.discover(start_dir='tests', pattern='test_*.py')        with open('test_report.html', 'wb') as report_file:        runner = HTMLTestRunner(stream=report_file, title='Test Report', description='Test Results')        result = runner.run(suite)
  • XMLTestRunner:这是另一个第三方库,用于生成 XML 格式的测试报告。
import unittestfrom xmlrunner import XMLTestRunnerif __name__ == '__main__':    loader = unittest.defaultTestLoader    suite = loader.discover(start_dir='tests', pattern='test_*.py')        with open('test_report.xml', 'wb') as report_file:        runner = XMLTestRunner(output=report_file)        result = runner.run(suite)

你也可以自定义测试运行器。继承 unittest.TestRunner 类并实现 run() 方法,以创建自己的测试运行器。QgV28资讯网——每日最新资讯28at.com

import unittestclass MyTestRunner(unittest.TextTestRunner):    def run(self, test):        print("Running tests with MyTestRunner")        result = super().run(test)        return resultif __name__ == '__main__':    loader = unittest.defaultTestLoader    suite = loader.discover(start_dir='tests', pattern='test_*.py')        runner = MyTestRunner()    result = runner.run(suite)

通常使用 HTMLTestRunner 即可满足需求,它非常易用。QgV28资讯网——每日最新资讯28at.com

实战一个测试案例

假设有一个测试函数 login:QgV28资讯网——每日最新资讯28at.com

# login.pydef login(username, password):    """模拟登录校验"""    if username == 'kira' and password == '123456':        return {"code": 0, "msg": "登录成功"}    else:        return {"code": 1, "msg": "账号或密码不正确"}

设计用例

根据函数的参数和逻辑,设计如下用例:QgV28资讯网——每日最新资讯28at.com

QgV28资讯网——每日最新资讯28at.com

编写测试用例并运行

import unittestfrom login import loginclass TestLogin(unittest.TestCase):    def test_login_correct(self):        """测试账号密码正确"""        test_data = {"username": "kira", "password": "123456"}        expect_data = {"code": 0, "msg": "登录成功"}        res = login(**test_data)        self.assertEqual(res, expect_data)    def test_login_wrong_password(self):        """测试账号正确密码不正确"""        test_data = {"username": "kira", "password": "123"}        expect_data = {"code": 1, "msg": "账号或密码不正确"}        res = login(**test_data)        self.assertEqual(res, expect_data)    def test_login_wrong_username(self):        """测试账号错误密码正确"""        test_data = {"username": "kir", "password": "123456"}        expect_data = {"code": 1, "msg": "账号或密码不正确"}        res = login(**test_data)        self.assertEqual(res, expect_data)if __name__ == '__main__':    unittest.main()

这是一个简单的测试用例,包含了三个测试函数。运行测试用例后,会输出测试结果,看完是否觉得unittest非常简单易用。ner.run(suite)QgV28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-10432-0.html不拼花哨,只拼实用:Unittest指南,干货为王!

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 基于范围的 for 循环:现代 C++ 循环控制的新思维

下一篇: 2023年需求最大的八种编程语言

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • 6月iOS设备性能榜:M2稳居榜首 A系列只能等一手3nm来救

    没有新品发布,自然iOS设备性能榜的上榜设备就没有什么更替,仅仅只有跑分变化而产生的排名变动,毕竟苹果新品的发布节奏就是这样的,一年下来也就几个移动端新品,不会像安卓厂商,一
  • 8月总票房已突破10亿!《封神》第一:口碑已经成了

    8月5日消息,据灯塔专业版数据,截至8月5日9时35分,8月总票房(含预售)已突破10亿。其中,《封神》以大比分的优势领先。根据官方消息,目前该片总票房已经超过14.
  • 如何正确使用:Has和:Nth-Last-Child

    我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会
  • 一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • Temu起诉SHEIN,跨境电商战事升级

    来源 | 伯虎财经(bohuFN)作者 | 陈平安日前据外媒报道,拼多多旗下跨境电商平台Temu正对竞争对手SHEIN提起新诉讼,诉状称Shein“利用市场支配力量强迫服装厂商与之签订独家
  • 华为发布HarmonyOS 4:更好玩、更流畅、更安全

    在8月4日的华为开发者大会2023(HDC.Together)大会上,HarmonyOS 4正式发布。自2019年发布以来,HarmonyOS一直以用户为中心,经历四年多的发展HarmonyOS已
  • 苹果、三星、惠普等暂停向印度出口笔记本和平板电脑

    集微网消息,据彭博社报道,在8月3日印度突然禁止在没有许可证的情况下向印度进口电脑/平板及显示器等产品后,苹果、三星电子和惠普等大公司暂停向印度
  • 由于成本持续增加,笔记本产品价格预计将明显上涨

    根据知情人士透露,由于材料、物流等成本持续增加,笔记本产品价格预计将在2021年下半年有明显上涨。进入6月下旬以来,全球半导体芯片缺货情况加剧,显卡、处理器
Top