首页 > 其他分享 >不拼花哨,只拼实用:unittest指南,干货为王!

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

时间:2023-08-25 15:44:05浏览次数:40  
标签:__ unittest 干货 测试 test 花哨 测试用例 self

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() 方法运行测试用例,按照函数名的排序执行测试。

以下是一个简单的例子:

import unittest

def 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()

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

unittest核心概念

测试脚手架

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

import unittest

class 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()

  1. setUp():每个测试方法运行前执行,用于测试前置的初始化工作。

  2. tearDown():每个测试方法结束后执行,用于测试后的清理工作。

  3. setUpClass():所有的测试方法运行前执行,用于单元测试类运行前的准备工作。使用 @classmethod 装饰器装饰,整个测试类运行过程中只会执行一次。

  4. tearDownClass():所有的测试方法结束后执行,用于单元测试类运行后的清理工作。使用 @classmethod 装饰器装饰,整个测试类运行过程中只会执行一次。

测试用例

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

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

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

断言方法

以下是常用的断言方法:

  • 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() 使用和上述类似。

import sys
import unittest

class 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)

测试套件

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

  1. 通过 unittest.main() 方法直接加载单元测试的测试模块,这是一种简单的加载方式。所有测试用例的执行顺序按照方法名的字符串表示的 ASCII 码升序排序,通过命名时使用 test_01_xxx 来指定执行顺序。

  2. 将所有的单元测试用例 TestCase 加载到测试套件 Test Suite 集合中,然后一次性加载所有测试对象。

通过 TestSuite 对象收集

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

import unittest

class 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 suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

通过 TestLoader 对象收集

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

import unittest

if __name__ == '__main__':
    loader = unittest.defaultTestLoader
    
    # 自动加载当前模块中所有以 'test_' 开头的测试用例函数
    suite = loader.loadTestsFromModule(__name__)

    runner = unittest.TextTestRunner()
    runner.run(suite)

import unittest

class 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 unittest

if __name__ == '__main__':
    loader = unittest.defaultTestLoader
    
    # 自动加载指定名称的测试用例
    suite = loader.loadTestsFromName('module.MyTestCase.test_addition')

    runner = unittest.TextTestRunner()
    runner.run(suite)

import unittest

if __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)

测试运行器

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

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

if __name__ == '__main__':
    loader = unittest.defaultTestLoader
    suite = loader.discover(start_dir='tests', pattern='test_*.py')
    
    runner = unittest.TextTestRunner()
    result = runner.run(suite)

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

if __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 unittest
from xmlrunner import XMLTestRunner

if __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() 方法,以创建自己的测试运行器。

import unittest

class MyTestRunner(unittest.TextTestRunner):
    def run(self, test):
        print("Running tests with MyTestRunner")
        result = super().run(test)
        return result

if __name__ == '__main__':
    loader = unittest.defaultTestLoader
    suite = loader.discover(start_dir='tests', pattern='test_*.py')
    
    runner = MyTestRunner()
    result = runner.run(suite)

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

实战一个测试案例

假设有一个测试函数 login

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

设计用例

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

序号 标题 测试数据 预期结果 实际结果
1 账号密码正确
2 账号正确密码不正确
3 账号错误密码正确

编写测试用例并运行

import unittest
from login import login

class 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)

总结

以上就是勇哥今天为各位小伙伴准备的内容,如果你想了解更多关于Python自动化测试的知识和技巧,欢迎关注我:公众号\博客\CSDN\B站:测试玩家勇哥;我会不定期地分享更多的精彩内容。感谢你的阅读和支持!


题外话,勇哥打算把新建的技术交流群,打造成一个活跃的高质量技术群。工作中遇到的技术问题,都可以在里面咨询大家,还有工作内推的机会。有兴趣的小伙伴,欢迎加我(记得备注是进群还是报名学习)

长按上方二维码2秒,关注我


勇哥,10年落魄测试老司机,技术栈偏python,目前在一家超大型房产公司担任自动化测试主管,日常工作比较繁杂,主要负责自动化测试,性能测试、软件质量管理及人员管理。工作之余专注于为粉丝进行简历修改、面试辅导、模拟面试、资料分享、一对一自动化测试教学辅导等副业发展。目前已服务十多位小伙伴,取得高薪offer。

关注公众号,测试干货及时送达

往期精选文章:
接口自动化测试项目2.0,让你像Postman一样编写测试用例,支持多环境切换、多业务依赖、数据库断言等
揭秘抓包利器:Python和Mitmproxy让您轻松实现接口请求抓取与分析!
构建高效的接口自动化测试框架思路
Pytest 快速入门
接口自动化之测试数据动态生成并替换
requests模块该如何封装?
接口自动化如何封装mysql操作
一文看懂python如何执行cmd命令
最通俗易懂python操作数据库
python-Threading多线程之线程锁
python正则一篇搞掂
性能测试之必备知识

性能分析思

Python + ChatGPT来实现一个智能对话的钉钉机器人
一文看懂python如何执行cmd命令

标签:__,unittest,干货,测试,test,花哨,测试用例,self
From: https://www.cnblogs.com/Nephalem-262667641/p/17650309.html

相关文章

  • # yyds干货盘点 # 通过pandas读取excel的数据,但是读取的结果显示后面四位变了?
    大家好,我是皮皮。一、前言前几天在Python最强王者群【wen】问了一个Pandas数据处理的问题,一起来看看吧。请教:通过pandas读取exlce的数据,其中,A列的数据为账号数字,原数据为6226093585801315,但是读取的结果显示6226093585800672,后面四位变了。df=pd.read_excel('销售数据.xlsx').conb......
  • 【干货】华为云图数据库GES技术演进
    本文分享自华为云社区《【干货】华为云图数据库GES技术演进》,作者:Chenyi。1背景大规模图数据无处不在,图查询、分析和表示学习已成为大数据和AI的核心部分之一。特别是知识图谱和图神经网络的发展,Graph已成为未来AI的基础。 各式各样的图数据 面向未来,图数据库在数据规......
  • # yyds干货盘点 # 盘点一个pandas读取excel数据并处理的小需求
    大家好,我是皮皮。一、前言前几天在Python最强王者群【wen】问了一个pandas数据处理的问题,一起来看看吧。通过pandas读取excel数据,其中两列是交易的备注信息,对A列数据筛选并把结果输出到C列。如果A列中有['吉利','奔驰','福特']三个字段,C列标记为‘汽车品牌’,如果A列有['NIKE','李宁......
  • 营销干货 | CPM和oCPM有啥区别?
    我们之前用了三个章节的篇幅,讲明白了广告投放的正确方法、渠道选择,以及什么是全域营销。从本章开始,我们的内容将侧重于一些在广告投放领域需要重点学习、关注的专业术语、方法论,并解答一些来自读者的留言和常见的问题,希望可以助你在从事广告投放、市场营销等相关工作时,事半功倍,业......
  • #yyds干货盘点# LeetCode程序员面试金典:完全二叉树的节点个数
    题目:给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。 示例1:输入:r......
  • #yyds干货盘点#Nginx 之 Location
    语法介绍location[=|~|~*|^~|@]uri{...}location@name{...}一个location关键字,后面跟着可选的修饰符(就是[]中间的正则),后面是要匹配的字符,花括号中是要执行的操作。=:表示精确匹配~:表示区分大小写正则匹配~*:表示不区分大小写正则匹配^~:表示URI以某个常规字符串开头!~:表......
  • # yyds干货盘点 # Pandas实战——灵活使用pandas基础知识轻松处理不规则数据
    大家好,我是皮皮。一、前言前几天在Python最强王者群【wen】问了一个pandas数据合并处理的问题,一起来看看吧。他的原始数据如下所示:然后预期的结果如下所示:二、实现过程这里【瑜亮老师】给了一个指导如下:原始数据中包含所有所需的信息,但是因为源系统导出的格式问题,有些数据被分配到......
  • #yyds干货盘点# LeetCode程序员面试金典:是子序列
    1.简述:给定字符串s 和t,判断 s 是否为 t 的子 序列。字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,是的一个子序列,而不是)。"ace""abcde""aec"进阶:如果有大量输入的S,称作S1,S2,...,Sk其中k>=10亿,你需要依次......
  • #yyds干货盘点# LeetCode程序员面试金典:最大正方形
    题目:在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。 示例1:输入:matrix=[["1","0","1","0","0"],["1","0","1","1","1"],["1&qu......
  • #yyds干货盘点#electron-egg: 当代桌面开发框架
    当前技术社区中出现了各种下一代技术或框架,却很少有当代可以用的,于是electron-egg就出现了。当前桌面软件技术有哪些?语言技术优点缺点C#wpf专业的桌面软件技术,功能强大学习成本高Javaswing/javaFx跨平台和语言流行GUI库少,界面不美观C++Qt跨平台,功能和类库丰富学习成本高Swift无非跨......