欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python之unittest框架的介绍及使用

程序员文章站 2024-03-21 23:23:46
...

unittest介绍

unittest是Python自带的一个单元测试框架, 它可以做单元测试, 也能用于编写和运行重复的测试工作.
它给自动化测试用例开发和执行提供了丰富的断言方法, 判断测试用例是否通过, 并最终生成测试结果.

TestCase编写

  • TestCase指的就是测试用例
  • 测试类必须继承unittest.TestCase
  • 测试方法名称命名必须以test开头
  • 测试方法的执行顺序有Case序号决定, 并非由代码顺序决定

一个简单的例子:

# 1. 导入unittest
import unittest


# 2. 创建类继承unittest.TestCase
class Test(unittest.TestCase):
    # 3. 创建测试用例方法, 方法要以test开头
    # 执行顺序是根据case序号来的, 并非代码的顺序
    def test_add_01(self):
        print(3+2)

    def test_add_02(self):
        print(10+5)

TestSuite与TextTestRunner

在上面提到, 测试用例的执行顺序默认是由case序号决定的,但也可以使用TestSuite控制用例的执行顺序。

  • TestSuite(测试套件)可以组织多个测试用例
  • TextTestRunner测试用例运行器
    • run()方法是测试用例的执行, 入参为suite测试套件

一个简单的例子

# 1. 导入unittest
import unittest


# 2. 创建类继承unittest.TestCase
class Test(unittest.TestCase):
    # 3. 创建测试用例方法, 方法要以test开头
    # 执行顺序是根据case序号来的, 并非代码的顺序
    def test_add_01(self):
        print(3+2)

    def test_add_02(self):
        print(10+5)

if __name__ == '__main__':
    suite = unittest.TestSuite()  # 实例化TestSuite
    suite.addTest(Test("test_add_02"))  # 添加测试用例
    suite.addTest(Test("test_add_01"))
    runner = unittest.TextTestRunner()  # 实例化TextTestRunner
    runner.run(suite)  # 传入suite并执行测试用例

但是要注意, 如果在Pychram中直接右键运行, 它是不会执行main下面的内容, 需要修改pychram的运行方式
Python之unittest框架的介绍及使用
然后重新运行, 就会执行main下面的内容了.

重点,通常测试用例都是多个的,因此测试用例可以存放到列表中一次性添加到测试套件。这里可以使用suite.addTests()

case_list = ['Test("test_add_02")', Test("test_add_01")]
suite.addTests(case_list)

TestLoader

如果有多个测试文件时, 可以使用TestLoader加载测试用例

  • unittest.defaultTestLoader, 使用discover()去加载测试用例
    • 找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到文件名才能被加载
  • discover(start_dir, pattern=‘test*.py’, top_level_dir=None)
    • start_dir:要测试的模块名货测试用例目录
    • pattern=“test*.py”: 表示用例文件名的匹配原则。 此处匹配文件名以“test”开头的“.py”类型的文件,*表示任意字符
    • top_level_dir=None:测试模块的顶层目录, 如果没有顶层目录, 默认为None

一个小例子, 再目录下创建多个test开头的py文件, 并且写入测试方法
Python之unittest框架的介绍及使用
然后使用discover()方法读取测试用例并执行

import unittest
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")
# 2.TextTestRunner运行用例
runer = unittest.TextTestRunner()
runer.run(suite)

TestFixture

setUp和tearDown

setUp()方法

  • 主要是用来初始化测试环境, 它在每条测试用例执行前都会调用

tearDown()方法

  • 主要作用是测试用例执行完毕后恢复测试环境, 即使出现异常也会调用此方法,每条用例执行结束后都会运行

一个简单的例子

import unittest

class Test(unittest.TestCase):
    def setUp(self) -> None:  # 调用setUp
        super().setUp()
        print("测试用例执行前操作")

    def test_add_01(self):
        print("num02")

    def test_add_02(self):
        print("num03")

    def tearDown(self) -> None:  # 调用tearDown
        super().tearDown()
        print("测试用例执行后操作")


# 返回结果
测试用例执行前操作
num02
测试用例执行后操作
测试用例执行前操作
num03
测试用例执行后操作

在上面的返回结果中可以明确的看出, 每条用例执行前都会先运行setUp,执行结束后都会运行tearDown

setUpClass和tearDownClass

setUpClass

  • 初始化测试环境且只会执行一次。在类中需要加上@classmethod

tearDownClass

  • 恢复测试环境且只会执行一次。在类中需要加上@classmethod
import unittest

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        print("测试前的操作")

    @classmethod
    def tearDownClass(cls) -> None:
        super().tearDownClass()
        print("测试后的操作")

    def test_add_01(self):
        print(3+2)

    def test_add_02(self):
        print(10+5)

# 返回结果
测试前的操作
15
5
测试后的操作

从返回结果可以看到setUpClass和tearDownClass都只是执行了一次,与setUp和tearDown的结果有很明显的区别。

注意,setUpClass和tearDownClass执行一次是针对当前的测试类而言的,如果当前的py文件有多个测试类, 那么它每个测试类都会执行一次。
如下:

# 1. 导入unittest
import unittest


# 2. 创建类继承unittest.TestCase
class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        print("测试前的操作")

    @classmethod
    def tearDownClass(cls) -> None:
        super().tearDownClass()
        print("测试后的操作")

    # 3. 创建测试用例方法, 方法要以test开头
    # 执行顺序是根据case序号来的, 并非代码的顺序

    def test_add_01(self):
        print(3+2)

    def test_add_02(self):
        print(10+5)


class Test2(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        print("测试前的操作2")

    @classmethod
    def tearDownClass(cls) -> None:
        super().tearDownClass()
        print("测试后的操作2")

    def test_add_03(self):
        print(20)

    def test_add_04(self):
        print(40)

if __name__ == '__main__':
    suite = unittest.TestSuite()  # 实例化TestSuite
    suite.addTests([Test("test_add_02"), Test("test_add_01"), Test2("test_add_03"), Test2("test_add_04")])  # 添加测试用例
    runner = unittest.TextTestRunner()  # 实例化TextTestRunner
    runner.run(suite)  # 传入suite并执行测试用例

断言(重点)

常用的断言

方法 检查
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) x的布尔值为真
assertFalse(x) x的布尔值为假
assertIn(a, b) a in b
assertNotIn(a, b) a not in b

assertEqual(a, b) 和 assertNotEqual(a, b)

# 1. 导入unittest
import unittest

# 2. 创建类继承unittest.TestCase
class Test(unittest.TestCase):
    def setUp(self) -> None:
        super().setUp()
        print("测试开始")

    def test_add_01(self):
        print("1 == 1")
        self.assertEqual(1, 1)  # 成功

    def test_add_02(self):
        print("1 == 2")
        self.assertEqual(1, 2)  # 失败

    def test_add_03(self):
        print("1 !=2 ")
        self.assertNotEqual(1, 2)  # 成功

    def test_add_04(self):
        print("1 != 1")
        self.assertNotEqual(1, 1)  # 失败

    def tearDown(self) -> None:
        super().tearDown()
        print("测试结束")

assertTrue(x)和assertFalse(x)

class Test(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        print("测试前的操作2")

    @classmethod
    def tearDownClass(cls) -> None:
        super().tearDownClass()
        print("测试后的操作2")

    def test_05(self):
        self.assertTrue(1 < 2)  # 成功

    def test_06(self):
        self.assertTrue(1 > 2)  # 失败

    def test_07(self):
        self.assertFalse(1 > 2)  # 成功

    def test_08(self):
        self.assertFalse(1 < 2)  # 失败

assertIn(a, b)和assertNotIn(a, b)

import unittest

class Test(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        print("测试前的操作2")

    @classmethod
    def tearDownClass(cls) -> None:
        super().tearDownClass()
        print("测试后的操作2")

    def test_09(self):
        self.assertIn("a", "abc")  # 成功

    def test_10(self):
        self.assertIn("a", "bcd")  # 失败

    def test_11(self):
        self.assertNotIn("a", "bcd")  # 成功

    def test_12(self):
        self.assertNotIn("a", "abc")  # 失败

测试报告

文件方式的测试报告

import unittest
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")
# 打开文件对象
with open("test_report.txt", "a") as f:
    # TextTestRunner运行用例
    runer = unittest.TextTestRunner(stream=f, verbosity=2)  # verbosity=2 输出详细日志
    runer.run(suite)

HTML方式的测试报告

HTML报告需要导入HTMLTestRunner.py,这个文件原本只支持python2,python3需要修改部分代码才能使用。
然后我发现CSDN这里已经有人上传过这个资源了, 不过貌似要收费, 我这里也传一个网盘吧, 这是已经修改好支持python的, 直接使用就行。

链接:https://pan.baidu.com/s/171jiRp9lw8Gtrl140g0uJw 
提取码:64db

回到正题,HTMLTestRunner的用法其实和TextTestRunner的差不多, 代码如下:

import unittest
from lib.HTMLTestRunner import HTMLTestRunner
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")

with open("test_report.html", "wb") as report:
    runner = HTMLTestRunner(stream=report, verbosity=2, title="HTML测试报告", description="这是练习的测试报告")
    runner.run(suite)

运行结束后在本地就会看到多了一个HTML文件,打开就能看到测试报告了
Python之unittest框架的介绍及使用

skip跳过用例

在遇到不想执行的测试用例时,可以使用skip方法

  • @unittest.skip(reason) :无条件跳过用例, reason是说明原因
  • @unittest.skipIf(condition, reason):condition为true时跳过用例
  • @ unittest.skipUnless(condition, reason):condition为False的时候跳过

实例代码:

# 1. 导入unittest
import unittest

class Test(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        print("测试前的操作2")

    @classmethod
    def tearDownClass(cls) -> None:
        super().tearDownClass()
        print("测试后的操作2")
        
    @unittest.skip("无条件跳过")
    def test_09(self):
        self.assertIn("a", "abc")
        
    @unittest.skipIf(2>1, "因为2>1所以跳过")
    def test_10(self):
        self.assertIn("a", "bcd")
        
    @unittest.skipUnless(1>2, "因为1>2为假,所以跳过")
    def test_11(self):
        self.assertNotIn("a", "bcd")

    def test_12(self):
        self.assertNotIn("a", "abc")

然后执行用例并生成测试报告

import unittest
from lib.HTMLTestRunner import HTMLTestRunner
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")

with open("test_report.html", "wb") as report:
    runner = HTMLTestRunner(stream=report, verbosity=2, title="HTML测试报告", description="这是练习的测试报告")
    runner.run(suite)

测试报告,只剩下了一个测试用例了
Python之unittest框架的介绍及使用