Python单元测试模块unittest

rh-li / 2024-10-23 / 原文

目录
  • 1. 基本用法
  • 2. 一个测试函数中可以包含多个断言
  • 3. assertTrue断言和assertRaises断言
  • 4. 例子
  • 5. 为什么不用print而用unittest

1. 基本用法

假设有一个函数add

def add(x, y):
    return x + y

如果我们想要测试这个函数的正确性,那么可以通过单元测试模块unittest来进行

下面通过代码来讲解unittest的用法

#导入模块
import unittest

# 首先创造一个测试类,这个类继承于unittest.TestCase
# 测试类的名字没有要求,但一般习惯于写成Test+<想要测试的类名>/<想要测试的函数名>的形式
class TestAdd(unittest.TestCase):

    # 编写测试函数:注意,测试函数必须以test开头,unittest 框架自动识别并运行这些方法。
    # 例如,如果函数名称改为add_test,那么unittest不会识别出这个方法
    def test_add(self):
        self.assertEqual(add(1, 2), 3) #检查add(1,2)是否等于3,这是unittest内置的一种断言

# 运行上面的测试类
# 下面这两行代码是固定的套路,记住就可以
if __name__ == '__main__':
    unittest.main()

运行上面的代码,得到结果

image-20241018093338774

第一行表明,unittest识别并且运行了一个测试函数(即test_add

第二行表明,所有的测试函数都通过了

2. 一个测试函数中可以包含多个断言

当然,在上面的测试函数test_add中,如果我们想要测试多组数字,可以在test_add中创建多个断言

import unittest

class TestAdd(unittest.TestCase):

	def test_add(self):
        # 真的断言
        self.assertEqual(add(1, 2), 3) 
        self.assertEqual(add(-1, 1), 0)
        self.assertEqual(add(-1, -1), -2)
        
        # 假的断言
        self.assertEqual(add(3, 4), 5) 


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

在上面的代码中,我们创建了三个真的断言和一个假的断言。

运行代码,看看会发生什么

可以看到,unittest显示,在位于image-20241018094507132的文件中,第19行,test_add函数中,self.assertEqual(add(3, 4), 5) 没有通过。

并且指出了断言没有通过的原因:add(3,4)等于7,7!=5,所以断言没有通过。

3. assertTrue断言和assertRaises断言

当然,除了assertEqual断言之外,unittest还提供了assertTrue断言,使用方法为

self.assertTrue(x) # 来判断x是否为真

以及判断异常的断言assertRaises,使用方法为

# 当执行代码的时候, 判断抛出的异常是否为我们想要的Error类型
with self.assertRaises(<Error 类型>):
    <想要测试的代码>

4. 例子

下面,我们看一个例子,综合使用assertEqual,assertTrue以及assertRaises三种断言。

首先,我们编写一个银行账户类

class BankAccount:
    def __init__(self, balance=0):
        """初始化
        
        Args:
        	balance:银行账户的余额
        """
        
        if balance < 0:
            raise ValueError("Initial balance cannot be negative")
        self.balance = balance

    def deposit(self, amount):
        """存钱函数
        
        Args:
        	amount: 想要存入的金额。如果amount<0,那么抛出ValueError异常
        """
        
        if amount <= 0:
            raise ValueError("Deposit amount must be positive")
        self.balance += amount

    def withdraw(self, amount):
        """取钱函数
        
        Args: 
        	amount:想要取出的金额。如果amount大于当前余额,那么抛出ValueError异常。
        """
        
        if amount > self.balance:
            raise ValueError("Insufficient funds")
        self.balance -= amount

    def is_overdrawn(self):
        """判断账户是否透支
        """
        
        return self.balance < 0

下面,为银行账户类编写测试类

一般来说,对类进行测试的时候,我们会在测试类中编写一个setUp方法,用来实例化想要测试的类。

import unittest

class TestBankAccount(unittest.TestCase):
    
    def setUp(self):
        # 初始化一个初始余额为100的账户
        self.account = BankAccount(100)

    def test_initial_balance(self):
        # 使用 assertEqual 验证初始余额
        self.assertEqual(self.account.balance, 100)

    def test_deposit(self):
        # 存入50元,使用 assertEqual 验证余额变化
        self.account.deposit(50)
        self.assertEqual(self.account.balance, 150)

    def test_withdraw(self):
        # 提取30元,使用 assertEqual 验证余额变化
        self.account.withdraw(30)
        self.assertEqual(self.account.balance, 70)

    def test_overdraw(self):
        # 使用 assertRaises 验证提取超额时会抛出异常
        with self.assertRaises(ValueError):
            self.account.withdraw(200)

    def test_deposit_negative_amount(self):
        # 使用 assertRaises 验证存入负金额时抛出异常
        with self.assertRaises(ValueError):
            self.account.deposit(-50)

    def test_is_overdrawn(self):
        # 提取所有余额,验证账户是否透支
        self.account.withdraw(100)
        self.assertTrue(self.account.is_overdrawn() == False)  # 账户余额为0,不应透支

    def test_initial_balance_cannot_be_negative(self):
        # 使用 assertRaises 验证初始余额不能为负
        with self.assertRaises(ValueError):
            BankAccount(-100)

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

assertEqual:

  • test_initial_balance 中,我们验证了初始余额是否为 100。
  • test_deposittest_withdraw 中,我们分别验证了存款和取款后余额是否与预期相符。

assertTrue:

  • test_is_overdrawn 中,我们使用了 assertTrue 来检查是否账户在余额为 0 时没有被透支。

assertRaises:

  • test_overdrawtest_deposit_negative_amount 中,我们验证当提取超过余额的金额或存入负数时,系统是否会抛出 ValueError
  • test_initial_balance_cannot_be_negative 中,我们检查初始化账户时余额不能为负,如果为负,应该抛出异常。

运行上面的代码,结果为

image-20241018100632961

表示测试全部通过。

5. 为什么不用print而用unittest

print 测试方式很难复用。一旦需要频繁修改测试内容,维护大量 print 语句将非常繁琐,测试逻辑和生产代码耦合在一起,代码会变得凌乱。

unittest 提供了结构化的测试方式,测试代码与生产代码分离。你可以很方便地更新测试内容,并确保每次修改代码时都有一套完整的测试用例来验证正确性。