unittest详解

前言

unittest原名为PyUnit,是由java的JUnit衍生而来。unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。unittest作为官方的测试框架,在测试方面非常基础,可以在此基础上进行二次开发。

unittest工作原理

unittest结构图:
在这里插入图片描述

  • test case :一个完整的测试单元,执行该测试单元可以完成对某一个问题的验证,完整体现在:测试前环境准备(setUp),执行测试代码(run),及测试后环境还原(tearDown);
  • test suite :多个测试用例的集合,测试套件或测试计划;
  • testLoader :加载TestCase到TestSuite中的,其中loadTestsFrom__()方法用于寻找TestCase,并创建它们的实例,然后添加到TestSuite中,返回TestSuite实例;
  • test runner :执行测试用例,并将测试结果保存到TextTestResult实例中,包括运行了多少测试用例, 成功了多少,失败了多少等信息;
  • test fixture:一个测试用例的初始化准备及环境还原,主要是setUp() 和 setDown()方法;

unittest简单示例

先通过一个简单的示例,对unittest有一个直观的了解:

import unittest

class Mytest1(unittest.TestCase):
    def setUp(self) -> None:
        print("===== setUp =====")

    def tearDown(self) -> None:
        print("===== tearDown =====")

    @classmethod
    def setUpClass(cls) -> None:
        print("===== setUpClass =====")

    @classmethod
    def tearDownClass(cls) -> None:
        print("===== tearDownClass =====")

    def test_add(self):
        print("测试函数add")
        self.name = "Aaa"

    def test_down(self):
        print("测试函数down")
        self.name = "Bbb"

# 文件内执行方式
if __name__ == "__main__":
    unittest.main()
    
# 命令行执行,scratch_11.py为包含
python -m unittest scratch_11.py

执行结果:

===== setUpClass =====
===== setUp =====
测试函数add
===== tearDown =====
===== setUp =====
测试函数down
===== tearDown =====
===== tearDownClass =====

通过上面的例子,我们可以总结:

  • setUpClass:整个测试开始后执行,只执行一次
  • tearDownClass:整个测试完成后执行,只执行一次
  • setUp:每运行一次用例前都会执行一次
  • tearDown:每运行一次用例后都会执行一次
  • 测试用例的命名规则为test_xxx,不以test_xxx命名的函数是方法,方法是不能被执行的

断言方法

测试中,我们会经常用到判断某个函数的返回结果是否等于某个值或者等于某个类型这种情况,非常适合使用断言来判断。在UnitTest中,TestCase 已经提供有封装好的断言方法进行断言校验。断言强调的是对于整个测试流程的结果进行判断,所以断言的内容是极为核心的。

  • assertEqual(a,b) a==b 值是否相等
  • aassertNotEqual(a,b) a!=b 值是否不相等
  • aasserIs(a,b) a is b 值是否相同
  • aassertIsNot(a,b) a is not b 值是否不同
  • assertIn(a,b) a in b a是否包含b
  • assertNotIn(a,b) a not in b a是否不包含b
  • ssertTrue(a) bool(a) is true 是否为真
  • assertFalse(a) bool(a)is false 是否为假
  • assertIsNone(a) a is None 是否为空
  • assertIsNotNone(a) a is None 是否不为空
  • assertIsInstance(a,b) Instance(a,b) a与b的数据类型一样
  • assertNotIsInstance(a) not Instance(a,b) a与b的数据类型不一样

unittest各种用法

UnitTest.skip

有时候,测试用例很多,有些需要执行,有些不想执行,那就需要跳过一些测试用例,这时候就需要用到skip,skip用法:

  • 无条件跳过,unittest.skip(“xxx”)
  • 条件为True跳过,unittest.skipIf(1 < 2, ‘xxx’)
  • 条件为False跳过,unittest.skipUnless(1 > 2, ‘xxx’)
  • 执行失败不计入case总数中,unittest.expectedFailure

示例如下:

import unittest

class Mytest1(unittest.TestCase):
    def setUp(self) -> None:
        print("===== setUp =====")

    def tearDown(self) -> None:
        print("===== tearDown =====")

    @classmethod
    def setUpClass(cls) -> None:
        print("===== setUpClass =====")

    @classmethod
    def tearDownClass(cls) -> None:
        print("===== tearDownClass =====")

    @unittest.skip("直接跳过")
    def test_1(self):
        print("test_1")

    @unittest.skipIf(1<2,"条件为True则跳过")
    def test_2(self):
        print("test_2")

    @unittest.skipUnless(1<2,"条件为False则跳过")
    def test_3(self):
        print("test_3")

    @unittest.expectedFailure
    def test_4(self):
        print("test_4")
        self.assertEqual(4,5)

if __name__ == "__main__":
    unittest.main()

执行结果如下,可以看到跳过了2个,对应test_1、test_2,test_3不满足条件,没有跳过,test_4执行错误,但是没有计入错误总数中:

ss.x
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK (skipped=2, expected failures=1)
===== setUp =====
test_3
===== tearDown =====
===== setUp =====
test_4
===== tearDown =====

结果中ss.x代表的意思:

  • . 表示通过 或者 pass
  • S 表示跳过
  • x 表示忽略错误
  • F False, 表示断言没有通过
  • E Error, 表示程序内部发生了错误

unittest.TestSuite

上面的TestCase例子中,4个测试方法的执行顺序是按照1 2 3 4的顺序,如果我们想自定义执行顺序怎么办,比如2可能依赖于1,在unittest中解决用例执行顺序的问题是使用TestSuite,测试套件TestSuite的作用:

  • 用于给测试用例进行排序
  • 管理测试用例

先看一个简单的示例:

from unittest import TestSuite

suite = TestSuite()

# 添加单个测试用例
suite.addTest(Mytest1("test_3"))
suite.addTest(Mytest1("test_1"))
suite.addTest(Mytest1("test_2"))

# 添加多个测试用例
cases = [Mytest1('test_3'), Mytest1('test_1'), Mytest1('test_2')]
suite.addTests(cases)

# 执行测试,unittest.TextTestRunner功能相当于unittest.main
runner = unittest.TextTestRunner()
runner.run(suite)

生成html格式测试报告

有时候我们需要更好的结果展示方式,可以使用HtmlTestRunner模块生成html格式的报告,安装后导入该模块,使用HTMLTestRunner代替默认的TextTestRunner()执行测试用例即可。

pip install html-testRunner安装HtmlTestRunner模块。

示例如下:

from unittest import TestSuite
from HtmlTestRunner import HTMLTestRunner

class Mytest1(unittest.TestCase):

    def test_1(self):
        print("test_1")

    def test_2(self):
        print("test_2")

    def test_3(self):
        print("test_3")

    def test_4(self):
        print("test_4")

if __name__ == "__main__":
    # 创建一个测试套件 suite
    suite = TestSuite()

    # 添加单个测试用例
    suite.addTest(Mytest1("test_3"))
    suite.addTest(Mytest1("test_1"))
    suite.addTest(Mytest1("test_2"))

    # 执行测试并输出html报告,output输出到xx目录下
    runner = HTMLTestRunner(output="result")
    runner.run(suite)

然后就可以在对应目录下查看执行的结果了:
在这里插入图片描述

ddt模块

有时我们需要对大量的数据进行同一种逻辑函数测试,这时候可以用到ddt模块。

ddt,是一种 数据驱动测试 思想,数据和用例进行分离,通过外部数据去生成测试用例,可以快速的对大量数据和不同情况进行测试。

首先我们需要在python中安装ddt模块:pip install ddt

数据引入的方式主要有以下几种:

  1. 通过@data装饰器的方式
  2. json文件
  3. yaml文件
  4. 其他
使用方法

照常先使用一个简单示例来演示:

使用data装饰器

import unittest
from ddt import ddt, data, unpack

# 先定义@ddt,用于表示要使用ddt了
@ddt
class Mytest1(unittest.TestCase):
    
    # data用于设定参数
    @data("a", "b", "c")
    def test_1(self, text):
        self.assertEqual("a", text)

if __name__ == "__main__":
    unittest.main()

使用json文件
先编写一个json文件:

[
    {
        "name": "test",
        "age": 18
    },
    {
        "name": "test1",
        "age": 20
    }
]

编写测试代码:

import unittest
from ddt import ddt, file_data


@ddt
class Mytest1(unittest.TestCase):

    @file_data("/Users/tangliqi/Desktop/个人文档/json文件/test.json")
    def test_1(self, **text):
        print(text)
        print(type(text))


if __name__ == "__main__":
    unittest.main()

输出结果:

{'name': 'test', 'age': 18}
<class 'dict'>
{'name': 'test1', 'age': 20}
<class 'dict'>

使用yaml文件

编写一个yaml文件:

# "-"表示一个列表

-
  name: 'test1'
  age:  18

-
  name: 'test2'
  age:  20

编写测试代码:

import unittest
from ddt import ddt, file_data

@ddt
class Mytest1(unittest.TestCase):

    @file_data("/Users/tangliqi/Desktop/个人文档/json文件/test.yaml")
    def test_1(self, **text):
        print(text)
        print(type(text))


if __name__ == "__main__":
    unittest.main()

执行结果:

{'name': 'test1', 'age': 18}
<class 'dict'>
{'name': 'test2', 'age': 20}
<class 'dict'>
  • 13
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tlqwanttolearnit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值