Python中断言Assertion的一些改进方案


Posted in Python onOctober 27, 2016

Python Assert 为何不尽如人意?

Python中的断言用起来非常简单,你可以在assert后面跟上任意判断条件,如果断言失败则会抛出异常。

>>> assert 1 + 1 == 2
>>> assert isinstance('Hello', str)
>>> assert isinstance('Hello', int)

Traceback (most recent call last):
 File "<input>", line 1, in <module>
AssertionError

其实assert看上去不错,然而用起来并不爽。就比如有人告诉你程序错了,但是不告诉哪里错了。很多时候这样的assert还不如不写,写了我就想骂娘。直接抛一个异常来得更痛快一些。

改进方案 #1

一个稍微改进一丢丢的方案就是把必要的信息也放到assert语句后面,比如这样。

>>> s = "nothin is impossible."
>>> key = "nothing"
>>> assert key in s, "Key: '{}' is not in Target: '{}'".format(key, s)

Traceback (most recent call last):
 File "<input>", line 1, in <module>
AssertionError: Key: 'nothing' is not in Target: 'nothin is impossible.'

看上去还行吧,但是其实写的很蛋疼。假如你是一名测试汪,有成千上万的测试案例需要做断言做验证,相信你面对以上做法,心中一定有千万只那种马奔腾而过。

改进方案 #2

不管你是你是搞测试还是开发的,想必听过不少测试框架。你猜到我要说什么了吧?对,不用测试框架里的断言机制,你是不是洒。

py.test

py.test 是一个轻量级的测试框架,所以它压根就没写自己的断言系统,但是它对Python自带的断言做了强化处理,如果断言失败,那么框架本身会尽可能多地提供断言失败的原因。那么也就意味着,用py.test实现测试,你一行代码都不用改。

import pytest

def test_case():
  expected = "Hello"
  actual = "hello"
  assert expected == actual

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

"""
================================== FAILURES ===================================
__________________________________ test_case __________________________________

  def test_case():
    expected = "Hello"
    actual = "hello"
>    assert expected == actual
E    assert 'Hello' == 'hello'
E     - Hello
E     ? ^
E     + hello
E     ? ^

assertion_in_python.py:7: AssertionError
========================== 1 failed in 0.05 seconds ===========================
""""

unittest

Python自带的unittest单元测试框架就有了自己的断言方法self.assertXXX() ,而且不推荐使用assert XXX语句。

import unittest

class TestStringMethods(unittest.TestCase):

  def test_upper(self):
    self.assertEqual('foo'.upper(), 'FoO')

if __name__ == '__main__':
  unittest.main()
  
"""
Failure
Expected :'FOO'
Actual  :'FoO'

Traceback (most recent call last):
 File "assertion_in_python.py", line 6, in test_upper
  self.assertEqual('foo'.upper(), 'FoO')
AssertionError: 'FOO' != 'FoO'
"""

ptest

我非常喜欢ptest,感谢Karl大神写了这么一个测试框架。ptest中的断言可读性很好,而且通过IDE的智能提示你能轻松完成各种断言语句。

from ptest.decorator import *
from ptest.assertion import *

@TestClass()
class TestCases:
  @Test()
  def test1(self):
    actual = 'foo'
    expected = 'bar'
    assert_that(expected).is_equal_to(actual)

"""
Start to run following 1 tests:
------------------------------
...
[demo.assertion_in_python.TestCases.test1@Test] Failed with following message:
...
AssertionError: Unexpectedly that the str <bar> is not equal to str <foo>.
"""

改进方案 #3

不仅仅是你和我对Python中的断言表示不满足,所以大家都争相发明自己的assert包。在这里我强烈推荐assertpy 这个包,它异常强大而且好评如潮。

pip install assertpy

看例子:

from assertpy import assert_that

def test_something():
  assert_that(1 + 2).is_equal_to(3)
  assert_that('foobar')\
    .is_length(6)\
    .starts_with('foo')\
    .ends_with('bar')
  assert_that(['a', 'b', 'c'])\
    .contains('a')\
    .does_not_contain('x')

从它的主页文档上你会发现它支持了几乎你能想到的所有测试场景,包括但不限于以下列表。

      Strings

      Numbers

      Lists

      Tuples

      Dicts

      Sets

      Booleans

      Dates

      Files

      Objects

而且它的断言信息简洁明了,不多不少。

Expected <foo> to be of length <4>, but was <3>.
Expected <foo> to be empty string, but was not.
Expected <False>, but was not.
Expected <foo> to contain only digits, but did not.
Expected <123> to contain only alphabetic chars, but did not.
Expected <foo> to contain only uppercase chars, but did not.
Expected <FOO> to contain only lowercase chars, but did not.
Expected <foo> to be equal to <bar>, but was not.
Expected <foo> to be not equal to <foo>, but was.
Expected <foo> to be case-insensitive equal to <BAR>, but was not.

在发现assertpy之前我也想写一个类似的包,尽可能通用一些。但是现在,我为毛要重新去造轮子?完全没必要!

总结

断言在软件系统中有非常重要的作用,写的好可以让你的系统更稳定。Python中默认的断言语句其实还有一个作用,如果你写了一个类型相关的断言,IDE会把这个对象当成这种类型,这时候智能提示就有如神助。

要不要把内置的断言语句换成可读性更好功能更强大的第三方断言,完全取决于实际情况。比如你真的需要验证某个东西并且很关心验证结果,那么必须不能用简单的assert;如果你只是担心某个点可能有坑或者让IDE认识某个对象,用内置的assert既简单又方便。

所以说,项目经验还是蛮重要的。以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能有所帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
python基于Tkinter库实现简单文本编辑器实例
May 05 Python
基于wxpython开发的简单gui计算器实例
May 30 Python
python 实现dict转json并保存文件
Dec 05 Python
python re模块匹配贪婪和非贪婪模式详解
Feb 11 Python
Python读取VOC中的xml目标框实例
Mar 10 Python
Python 3.10 的首个 PEP 诞生,内置类型 zip() 迎来新特性(推荐)
Jul 03 Python
python 图像插值 最近邻、双线性、双三次实例
Jul 05 Python
python3获取控制台输入的数据的具体实例
Aug 16 Python
python对 MySQL 数据库进行增删改查的脚本
Oct 22 Python
python 爬虫如何正确的使用cookie
Oct 27 Python
pytho matplotlib工具栏源码探析一之禁用工具栏、默认工具栏和工具栏管理器三种模式的差异
Feb 25 Python
一行Python命令实现批量加水印
Apr 07 Python
利用Python实现颜色色值转换的小工具
Oct 27 #Python
Python实现批量检测HTTP服务的状态
Oct 27 #Python
python解决网站的反爬虫策略总结
Oct 26 #Python
Python控制多进程与多线程并发数总结
Oct 26 #Python
Python网络爬虫项目:内容提取器的定义
Oct 25 #Python
Python实现ssh批量登录并执行命令
Oct 25 #Python
详解Python的Lambda函数与排序
Oct 25 #Python
You might like
使用 eAccelerator加速PHP代码的目的
2007/03/16 PHP
phpMyAdmin 安装教程全攻略
2007/03/19 PHP
thinkPHP中钩子的使用方法实例分析
2017/11/16 PHP
PHP长连接实现与使用方法详解
2018/02/11 PHP
js作用域及作用域链概念理解及使用
2013/04/15 Javascript
JS中批量给元素绑定事件过程中的相关问题使用闭包解决
2013/04/15 Javascript
jQuery点缩略图弹出层显示大图片
2015/02/13 Javascript
JavaScript中的getDay()方法使用详解
2015/06/09 Javascript
AngularJS的脏检查深入分析
2017/04/22 Javascript
js时间戳与日期格式之间转换详解
2017/12/11 Javascript
详解VUE2.X过滤器的使用方法
2018/01/11 Javascript
在 Linux/Unix 中不重启 Vim 而重新加载 .vimrc 文件的流程
2018/03/21 Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
2019/06/03 Javascript
vue中通过使用$attrs实现组件之间的数据传递功能
2019/09/01 Javascript
vue解决使用$http获取数据时报错的问题
2019/10/30 Javascript
Vue学习之axios的使用方法实例分析
2020/01/06 Javascript
[01:04:14]VP vs TNC 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
Google开源的Python格式化工具YAPF的安装和使用教程
2016/05/31 Python
python引入不同文件夹下的自定义模块方法
2018/10/27 Python
Django为窗体加上防机器人的验证码功能过程解析
2019/08/14 Python
Python time库基本使用方法分析
2019/12/13 Python
Pytorch的mean和std调查实例
2020/01/02 Python
Python 简单计算要求形状面积的实例
2020/01/18 Python
基于Python的自媒体小助手---登录页面的实现代码
2020/06/29 Python
Baby Tulai澳大利亚:美国婴儿背带品牌
2018/10/15 全球购物
解释下面关于J2EE的名词
2013/11/15 面试题
工程师岗位职责
2013/11/08 职场文书
毕业生个人的求职信范文
2013/12/03 职场文书
股东合作协议书
2014/09/12 职场文书
批评与自我批评发言稿
2014/10/15 职场文书
《詹天佑》教学反思
2016/02/20 职场文书
写自招自荐信的绝招!
2019/04/19 职场文书
python urllib库的使用详解
2021/04/13 Python
MySQL Shell的介绍以及安装
2021/04/24 MySQL
详解CSS不受控制的position fixed
2021/05/25 HTML / CSS
Python  序列化反序列化和异常处理的问题小结
2022/12/24 Python