Python 单元测试:assertTrue 和assertFalse 的真假

本文对比了单元测试中的布尔断言方法assertTrue和assertFalse与身份断言assertIs的区别。通过一个始终返回True的函数示例,展示了不同断言方法在面对实际代码变化时的表现差异。

导读Lightbot在这篇文章中,我们将介绍单元测试的布尔断言方法 assertTrue 和 assertFalse 与身份断言 assertIs 之间的区别。

Python 单元测试:assertTrue 和assertFalse 的真假Python 单元测试:assertTrue 和assertFalse 的真假

定义

下面是目前单元测试模块文档中关于assertTrue 和 assertFalse 的说明,代码进行了高亮:

assertTrue(expr, msg=None)
assertFalse(expr, msg=None)

测试该表达式是真值(或假值)。
注:这等价于"bool(expr) is True"而不等价于"expr is True"(后一种情况请使用 assertIs(expr, True))。
Mozilla 开发者网络中定义 真值 如下:
在一个布尔值的上下文环境中能变成“真”的值
在 Python 中等价于:

bool(expr) is True

这个和 assertTrue 的测试目的完全匹配。

因此该文档中已经指出 assertTrue 返回真值,assertFalse 返回假值。这些断言方法从接受到的值构造出一个布尔值,然后判断它。同样文档中也建议我们根本不应该使用 assertTrue 和 assertFalse。

在实践中怎么理解?

我们使用一个非常简单的例子 - 一个名称为 always_true的函数,它返回 True。我们为它写一些测试用例,然后改变代码,看看测试用例的表现。

作为开始,我们先写两个测试用例。一个是“宽松的”:使用 assertTrue来测试真值。另外一个是“严格的”:使用文档中建议的 assertIs 函数。

import unittest
from func import always_true

class TestAlwaysTrue(unittest.TestCase):
    def test_assertTrue(self):
        """
        always_true returns a truthy value
        """
        result = always_true()

        self.assertTrue(result)

    def test_assertIs(self):
        """
        always_true returns True
        """
        result = always_true()

        self.assertIs(result, True)

下面是func.py中的非常简单的函数代码:

def always_true():
    """
    I'm always True.

    Returns:
        bool: True
    """
    return True

当你运行时,所有测试都通过了:

always_true returns True ... ok
always_true returns a truthy value ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.004s

OK

开心ing~

现在,某个人将 "always_true" 函数改变成下面这样:

def always_true():
    """
    I'm always True.

    Returns:
        bool: True
    """
    return 'True'

它现在是用返回字符串"True" 来替代之前反馈的 True (布尔值)。(当然,那个“某人”并没有更新文档 - 后面我们会增加难度。)

这次结果并不如开心了:

always_true returns True ... FAIL
always_true returns a truthy value ... ok

======================================================================
FAIL: always_true returns True
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/assertttt/test.py", line 22, in test_is_true
    self.assertIs(result, True)
AssertionError: 'True' is not True

----------------------------------------------------------------------
Ran 2 tests in 0.004s

FAILED (failures=1)

只有一个测试用例失败了!这意味着 assertTrue 给了我们一个误判false-positive。在它不应该通过测试时,它通过了。很幸运的是我们第二个测试是使用 assertIs 来写的。

因此,跟手册上了解到的信息一样,为了保证 always_true 的功能和更严格测试的结果保持一致,应该使用 assertIs 而不是 assertTrue。

使用断言的辅助方法

使用 assertIs 来测试返回 True 和 False 来冗长了。因此,如果你有个项目需要经常检查是否是返回了 True 或者 False,那们你可以自己编写一些断言的辅助方法。

这好像并没有节省大量的代码,但是我个人觉得提高了代码的可读性。

def assertIsTrue(self, value):
    self.assertIs(value, True)

def assertIsFalse(self, value):
    self.assertIs(value, False)
总结

一般来说,我的建议是让测试越严格越好。如果你想测试 True 或者 False,听从文档的建议,使用 assertIs。除非不得已,否则不要使用 assertTrue 和 assertFalse。

如果你面对的是一个可以返回多种类型的函数,例如,有时候返回布尔值,有时候返回整形,那么考虑重构它。这是代码的异味。在 Python 中,抛出一个异常比使用 False 表示错误更好。

此外,如果你确实想使用断言来判断函数的返回值是否是真,可能还存在第二个代码异味 - 代码是正确封装了吗?如果 assertTrue 和 assertFalse 是根据正确的 if 语句来执行,那么值得检查下你是否把所有你想要的东西都封装在合适的位置。也许这些 if语句应该封装在测试的函数中。

测试开心!




本文原创地址:http://www.linuxprobe.com/python-unit.html

免费提供最新Linux技术教程书籍,为开源技术爱好者努力做得更多更好:http://www.linuxprobe.com/thread


### Python 自动化测试脚本编写教程 #### 使用 HttpRunner 进行接口自动化测试 对于希望深入了解如何通过 Python 实现接口自动化的开发者来说,HttpRunner 是一个非常受欢迎的选择。该框架不仅支持 HTTP 请求的发送与响应验证,还允许用户以 YAML 或 JSON 文件的形式定义测试用例,从而简化了复杂场景下 API 测试的工作流程[^1]。 ```yaml config: name: "test demo" variables: device_sn: "TEST_SN_001" teststeps: - name: get posts request: url: "/api/v1/posts" method: GET params: userId: $device_sn extract: post_id: "body[0].id" validate: - eq: ["status_code", 200] ``` 这段配置展示了如何设置请求参数、提取返回数据以及执行断言操作。这使得即使是没有深厚编程背景的人也能快速上手并构建起有效的API测试套件。 #### 探索多个 Python 自动化测试框架 除了专注于 Web API 的 HttpRunner 外,还有其他多种适用于不同需求场景的 Python 自动化测试解决方案可供选择: - **PyTest**: 提供了一个简单而强大的命令行界面用于运行测试,并且拥有丰富的插件生态系统; - **Robot Framework**: 支持关键字驱动的方式描述业务逻辑,非常适合团队协作项目中的应用; - **Selenium WebDriver (with Python bindings)**: 主要应用于Web应用程序的功能性UI层面交互模拟; 上述提到的各种工具都在各自的领域内提供了独特的优势功能集,可以根据具体项目的特性挑选最合适的技术栈组合[^2]。 #### PyUnit 单元测试框架详解 作为最早期被广泛采用的一个Python内置库之一,`unittest`,也常被称为 `PyUnit`. 它遵循xUnit架构模式,为程序员提供了一种标准化的方法来组织管理代码级别的单元测试案例。下面是一个简单的例子说明怎样创建继承自 TestCase 类别的类,并重写 setUp() 方法初始化资源,在 tearDown() 中清理环境变量等常见做法[^4]. ```python import unittest class TestStringMethods(unittest.TestCase): @classmethod def setUpClass(cls): print('setup class') def setUp(self): self.test_string = 'hello world' def test_upper(self): self.assertEqual(self.test_string.upper(), 'HELLO WORLD') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = self.test_string self.assertEqual(s.split(), ['hello', 'world']) if __name__ == '__main__': unittest.main() ``` 此段程序片段展示了一些典型的操作方式,比如使用 assertEqual 来比较预期结果同实际输出是否一致,或是借助 assertTrue assertFalse 对布尔表达式的真伪作出判断。此外还可以看到 setup/teardown 钩子函数的作用范围分别是在每次调用之前准备必要的前置条件之后恢复初始状态以便于下一个实例能够正常工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值