跳到正文
南歌 EuanSu
返回

Python中的单元测试

Python代码的单元测试

单元测试的概念

单元测试的策略

如果要创建单元测试,可以遵循如下基本技巧来确保涵盖所有的测试用例。

单元测试类的编写

  1. 首先编写要进行单元测试的代码。

    def add(x, y):
        """加法函数"""
        return x + y
    
    def subtract(x, y):
        """减法函数"""
        return x - y
    
    def multiply(x, y):
        """乘法函数"""
        return x * y
    
    def divide(x, y):
        """除法函数"""
        if y != 0:
            return x / y
        else:
            return ValueError("除数不能为0")
  2. 编写测试工具代码。

    # 测试代码应使用test_xxx的名称进行规范
    # 这里使用断言assert进行模拟
    # assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常
    # 如下代码执行正常不报错,则说明函数执行符合预期
    from myfunctions import divide
    
    # 一个简单的单元测试示例
    def test_divide(x, y, result):
        r = divide(x, y)
        assert result == r
    
    
    def test_divide_error(x, y):
        try:
            divide(x, y)
        except ValueError:
            assert False
  3. 执行单元测试的工具代码。

    单元测试执行完成,无错误输出代码执行符合预期。

    image-20240213234844432

Python中的单元测试类

如上的代码能够满足Python中进行单元测试的需求,但如果换一套方法,我们的单元测试又需要手动编码,Python中提供了专业的单元测试工具类:unittest,以下是该单元测试类的使用方法介绍。

unittest 将我们常规用到的测试场景封装了以下断言方法,根据测试所需要的场景进行引用。

断言方法方法解释
assertEqual(a, b)检查a 和b 是否相等。
assertNotEqual(a, b)检查a 和b 是否不相等。
assertTrue(x)检查x 是否为True。
assertFalse(x)检查x 是否为False。
assertIs(a, b)检查a 和b 是否为同一对象(is)。
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 类型的实例。
assertAlmostEqual(a, b)检查a 和b 是否近似相等(适用于浮点数比较)。
assertNotAlmostEqual(a, b)检查a 和b 是否不近似相等(适用于浮点数比较)。
assertRaises(Error, func, *args, **kwargs)检查当调用function 时是否抛出了Error 异常。

unittest模块的基础使用

单元测试代码:

# unittest 是python自带的工具包,无需单独下载
import unittest
from myfunctions import *

class TestMyFunctions(unittest.TestCase):

    def test_add(self):
        # assertEqual,断言测试方法,`unittest` 将我们常规用到的测试场景封装成断言方法,根据测试所需要的场景进行引用。
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(-1, 2), 1)
        self.assertEqual(add(-1, -2), -3)

    def test_subtract(self):
        self.assertEqual(subtract(1, 2), -1)
        self.assertEqual(subtract(-1, 2), -3)
        self.assertEqual(subtract(-1, -2), 1)

    def test_multiply(self):
        self.assertEqual(multiply(1, 2), 2)
        self.assertEqual(multiply(-1, 2), -2)
        self.assertEqual(multiply(-1, -2), 2)

    def test_divide(self):
        self.assertEqual(divide(1, 2), 0.5)
        self.assertEqual(divide(-1, 2), -0.5)
        self.assertEqual(divide(-1, -2), 0.5)

unittest 代码并不能够直接运行,有以下执行方法:

unittest模块的前置方法

在实际的测试中可能同时存在多个前置相同的测试,unittest模块提供了setUp()用于在测试开始前执行相关环境的设置,tearDown() 在 setUp() 方法之后进行执行。

注:这里的 setUp 和 tearDown 是固定的函数名,不允许更改。

要测试的代码:

def divide(x, y):
    """除法函数"""
    print("divide called")
    if y != 0:
        return x / y
    else:
        raise ValueError("除数不能为0")

测试工具类:

import unittest
from myfunctions import *

class TestMyFunctions(unittest.TestCase):

    def setUp(self):
        self.test_value_a = 10
        self.test_value_b = 5
        self.test_value_c = 0
        print("setUp called")

    def tearDown(self):
        del self.test_value_a
        del self.test_value_b
        del self.test_value_c
        print("tearDown called")

    def test_divide(self):
        self.assertEqual(divide(self.test_value_a, self.test_value_b), 2)
        self.assertRaises(ValueError, divide, self.test_value_a, self.test_value_c)

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

代码执行测试:

可以看到,在执行函数测试之前,首先调用了 setUp() 前置方法,然后执行测试,测试结束后,调用 tearDown() 清理单元测试。

image-20240214001944498

测试覆盖率

覆盖率是用来度量测试完整性的手段,是测试效果衡量的标准,是测试技术有效性的度量: 覆盖率 = (至少被执行一次的项目(item)数) / (项目的总数)

Python中提供了测试覆盖率的模块类:coverage,该第三方包需要手动安装。

image-20240214002749901

这里使用如下方法直接进行代码的测试覆盖率分析

# 调用单元测试工具类
coverage run -m unittest discover
# 输出测试报告
coverage report

image-20240214003203531

这里的代码测试覆盖率较低,对单元测试工具类进行优化,要测试的代码如下:

def add(x, y):
    """加法函数"""
    return x + y

def subtract(x, y):
    """减法函数"""
    return x - y

def multiply(x, y):
    """乘法函数"""
    return x * y

def divide(x, y):
    """除法函数"""
    print("divide called")
    if y != 0:
        return x / y
    else:
        raise ValueError("除数不能为0")

优化后的单元测试方法为:

import unittest
from myfunctions import *

class TestMyFunctions(unittest.TestCase):

    def setUp(self):
        self.test_value_a = 10
        self.test_value_b = 5
        self.test_value_c = 0
        print("setUp called")

    def tearDown(self):
        del self.test_value_a
        del self.test_value_b
        del self.test_value_c
        print("tearDown called")

    def test_add(self):
        self.assertEqual(add(self.test_value_a, self.test_value_b), 15)
        self.assertEqual(add(self.test_value_a, self.test_value_c), 10)

    def test_subtract(self):
        self.assertEqual(subtract(self.test_value_a, self.test_value_b), 5)
        self.assertEqual(subtract(self.test_value_a, self.test_value_c), 10)

    def test_multiply(self):
        self.assertEqual(multiply(self.test_value_a, self.test_value_b), 50)
        self.assertEqual(multiply(self.test_value_a, self.test_value_c), 0)

    def test_divide(self):
        self.assertEqual(divide(self.test_value_a, self.test_value_b), 2)
        self.assertRaises(ValueError, divide, self.test_value_a, self.test_value_c)

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

结果测试

image-20240214003718187


评论


上一篇
达内教育Django全套教程
下一篇
Linux主机部署Docker