高质量的代码离不开单元测试,而设计单元测试的用例往往又比较耗时,而且难以想到一些极端情况,本文讲述如何使用 Hypothesis 来自动化单元测试

刷过力扣算法题的同学都知道,有时候觉得代码已经很完善了,一提交才发现很多情况没有考虑到。然后感叹力扣的单元测试真的牛比。

因此,高质量的代码离不开单元测试,如果现在还没有写过单元测试,建议先去学习以下常用的单元测试库[1],只要实践过,才能感受到本文开头提到的那些痛点。

Hypothesis 是一个 Python 库,用于让单元测试编写起来更简单,运行时功能更强大,可以在代码中查找您不会想到的极端情况。它稳定,强大且易于添加到任何现有测试框架中。它的工作原理是让您编写断言每种情况都应该正确的测试,而不仅仅是您偶然想到的那些。

Hypothesis 的基础知识

典型的单元测试需要自己写一些测试用例,然后编写测试函数,通过一段代码运行它,然后根据预期结果检查结果。

Hypothesis 有所不同。它是基于属性进行单元测试。它通过生成与您的规范匹配的任意数据并检查在这种情况下程序是否仍然有效。如果找到了一个失败的用例,它将采用该示例并将其测试用例范围缩减缩减为一定尺寸,然后对其进行简化,直到找到一个仍会导致问题的小得多的示例。然后将其保存,后续单元测试时仍会使用这些用例。

现在就让我们看看怎么用吧。

Hypothesis 快速入门

1、安装

可以通过 pip 安装,也可以通过源代码安装[2],也可以安装一些扩展[3],如下:

pip install hypothesis
pip install hypothesis[pandas,django]

2、使用

先写一段代码,保存在 mycode.py 中,功能是对字符串进行特定的编码和解码,内容如下:

def encode(input_string):
 count = 1
 prev = ""
 lst = []
 for character in input_string:
  if character != prev:
   if prev:
    entry = (prev, count)
    lst.append(entry)
   count = 1
   prev = character
  else:
   count += 1
 entry = (character, count)
 lst.append(entry)
 return lst


def decode(lst):
 q = ""
 for character, count in lst:
  q += character * count
 return q

对这段代码进行单元测试,往往需要写很多测试用例,现在我们使用 hypothesis 来自动为我们测试,编写 test_mycode.py (文件名随意),内容如下:

from hypothesis import given
from mycode import decode,encode
from hypothesis.strategies import text
import unittest


class TestEncoding(unittest.TestCase):
 @given(text())
 def test_decode_inverts_encode(self, s):
  self.assertEqual(decode(encode(s)), s)


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

可以看出,这里并没有出现具体的测试用例,而是使用来 text 的策略,相当于 hypothesis 自动穷举来可能的情况,也可以看出它很容易可其他测试框架集成,这里是 unittest。现在来运行一下看看效果:

(py38env) ? tmp python test_mycode.py
Falsifying example: test_decode_inverts_encode(
 self=<__main__.TestEncoding testMethod=test_decode_inverts_encode>, s='',
)
E
======================================================================
ERROR: test_decode_inverts_encode (__main__.TestEncoding)
----------------------------------------------------------------------
Traceback (most recent call last):
 File "test_mycode.py", line 9, in test_decode_inverts_encode
 def test_decode_inverts_encode(self, s):
 File "/Users/aaron/py38env/lib/python3.8/site-packages/hypothesis/core.py", line 1162, in wrapped_test
 raise the_error_hypothesis_found
 File "test_mycode.py", line 10, in test_decode_inverts_encode
 self.assertEqual(decode(encode(s)), s)
 File "/Users/aaron/tmp/mycode.py", line 14, in encode
 entry = (character, count)
UnboundLocalError: local variable 'character' referenced before assignment

----------------------------------------------------------------------
Ran 1 test in 0.048s

FAILED (errors=1)

这里测试出当字符串为 '' 的时候会抛出 UnboundLocalError 的异常。现在我们来修复这个 bug,然后把所有的测试用例 s 给打印出来,看看它用了哪些测试用例。

encode 函数加入以下代码:

if not input_string:
 return []

test_mycode.py 文件打印出测试用例:

@given(text())
def test_decode_inverts_encode(self, s):
 print(f"{s=}")
 self.assertEqual(decode(encode(s)), s)

再次执行:

(py38env) ? tmp python test_mycode.py
s=''
s='1'
s='0'
s='0'
s='0'
s='ā'
s='\U000cf5e5'
s='0'
s=''
s='0'
s='0'
s='E'
s=")dù'\x18\U0003deb3¤jd"
s='\U0005bc37\x07\U000537a1Yà?i?\U000ce9e5\x0b'
s='\U0005bc37\U0005bc37\U000537a1Yà?i?\U000ce9e5\x0b'
s='\U0005bc37\U000537a1\U000537a1Yà?i?\U000ce9e5\x0b'
s='à\U000537a1\U000537a1Yà?i?\U000ce9e5\x0b'
s='\U000965e1\x12\x85&\U000f500a??c'
s='\n\U0004466c\x86?\x07'
s='ê\U00063f1e\x01G\x88'
s='úV\n'
s='VV\n'
s='\U0008debf??è'
s='\U0008debf??è'
s='\U0008debf??'
s='\U0008debf\U0008debf'
s='\U0008debf\U0008debfó]?àq\x82#\U00015196\U0001c8beg'
s='\U0008debfgó]?àq\x82#\U00015196\U0001c8beg'
s='?'
s='?'
s='?\U00085b9e'
s="?8'?\U00057c38ù;\x07\U000a5ea8ò?=\U00091d5b~8?"
s='\U000d6497Y>'
s='\U000e0f01'
s='\U000e0f01?0y¢KN®'
s='\U000e0f01?0y¢KN®'
s='\U00050a06'
s='?\U000b98b3か\U000ba80aá`?-êu\x8c\x903F?"'
s='\x8e\U0004612a\x83?'
s='\x8e'
s='\x8e\x98\U000fb3e0\U0010d2b3\x10\x82\x94D渥'
s='¥W'
s='p\U000e5a2aE·`ì'
s='\U000b80f8\x12\U000c2d54'
s='.\U000703de'
s='6\U00010ffa\U000f7994\x8e'
s='116\U000f7994\x8e'
s='1?6\U000f7994\x8e'
s='4?6\U000f7994\x8e'
s='4\x8e6\U000f7994\x8e'
s='0'
s='\U0006a564′D\x93ü\x9eb&i\x1c?'
s='\U000ceb6f'
s='\U000ceb6f\xa0\x08'
s='\U000ceb6f\xa0\x08'
s='\U000ceb6f?\x08'
s='\U000ceb6f?匀\U0007cc15\U000b2aaa×**'
s='\U000ceb6f?匀'
s='匀?匀'
s='J\x14??'
s='q)'
s='q)'
s='q\U00060931'
s='q6'
s='\U000e3441'
s='\U000e3441\U00019958ˉ'
s='\x13'
s='\U000f34dbk'
s='Kp&t?à'
s='\n?\x93'
s='\n\n\x93'
s='\U00019c8d?3\U00056cbd\U000e3b2f\U00058d302'
s='\x90=R\x8b?\x03'
s='\x9a'
s='\U000147e7'
s='\U000147e7\x85\U0007a3ef'
s='\U000147e7\U00050a070?>'
s='\U000a4089\x0eC+Rá\x02\x97\x9cüì?SS\U0006cbc5;?~\x16\x019V?\U000a32fdQ÷\x15'
s='Tú?\x19©Z®'
s='??'
s='\U000cd45a'
s='\U000cd45a\U000e15cb?\x08J\ueb3eú?\x07I\x91\x9a\x18\x16?\x80\x1a'
s='\x8f}o\x0eq\x0b'
s='\x0e}o\x0eq\x0b'
s="\U000e05a3&?o[f?\x8büR'í?t\x97íW\x05\U000caea9\U0008fd74\U000e8f1c1?df??\x13"
s='\x10\U000e12e2ù\U0006f96ery\U00014baf\x00\x95\U000dbc92é\U00081613μ\U0003b865Z\U0008cc3c'
s='ú\U000b561f\x8f?'
s='\tà?÷'
s='à\x92©ì\U000618fa\x92'
s='\U000aaf94\x94\x84\U000cda69\U0005291a\U000a63det?O\x8a>\U000b458bê.\U00086f07\x1a'
s='\U0009754e?U_\xa0\x13PQ\x18o\x07\U0006c9c5.á'
s='\U00102456'
s='3W??'
s='\x14\x1c'
s='\x14'
s='\x14\U00105bcd"\x10?\x99\U000a5032R\U00056c44V&÷>+\U000aaff2?®\U000d7570%a!\U00032553′8x^?'
s='\x00\U000e2ac4??UrB'
s='\x00\U000e2ac4??UrB'
s='\x00\U000e2ac4??UrB'
s='a\x1aU\x8a?\U000b2fb9\U0005a586'
.
----------------------------------------------------------------------
Ran 1 test in 0.180s

OK

从执行结果可以看出,'' 首先被测试,其次 hypothesis 使用了大量的极端测试用例,减轻了手写的负担,大大提升了效率。

虽然 hypothesis 具有自动记忆功能,你仍然可以显式的指定某个测试用例一直被测试,而且这是推荐的做法,比如我想在每次的测试中都测试 '',可以这样写:

from hypothesis import given, example
from hypothesis.strategies import text


@given(text())
@example("")
def test_decode_inverts_encode(s):
 assert decode(encode(s)) == s

这一点非常有用,提升了测试代码的可读性,可以用来告诉开发人员或者未来的自己,输入的字符串必须要考虑 '' 的情形。

此外,执行单元测试,不一定要使用 unittest.main(),也可以这样,是不是很方便:

if __name__ == "__main__":
 test_decode_inverts_encode()

3、其他策略参考

从哪里开始

以上仅仅是抛砖引玉,hypothesis 还有很多自动化的特性,不再一一列举,最好的学习方法是边做,边尝试。hypothesis 是一个开源项目,有着详细的官方文档[4],GitHub 仓库[5]这里都是你开启自动化测试的好地方:

参考资料

[1]

库: https://realpython.com/python-testing/

[2]

源代码安装: https://github.com/HypothesisWorks/hypothesis/blob/master/CONTRIBUTING.rst

[3]

扩展: https://hypothesis.readthedocs.io/en/latest/extras.html

[4]

官方文档: https://hypothesis.readthedocs.io/en/latest/quickstart.html#running-tests

[5]

GitHub 仓库: https://github.com/HypothesisWorks/hypothesis/

以上就是python 如何用 Hypothesis 来自动化单元测试的详细内容,更多关于python 用 Hypothesis 来自动化单元测试的资料请关注乐虎体育其它相关文章!

python 如何用 Hypothesis 来自动化单元测试的更多相关文章

  1. python将YUV420P文件转PNG图片格式的两种方法

    方法一:import osimport cv2 as cvimport numpy as np# 读取yuv420p的一帧文件,并转化为png图片if __name__ == '__main__':filepath = 'one_frame_of_highway.yuv'binfile = open......

  2. python中温度单位转换的实例方法

    温度有摄氏度和华氏度两个单位,我们通常使用的是摄氏度,对于转换成华氏度,很多小伙伴记不住公式。作为万能的计算机,它是可以帮助我们解决温度单位转换的问题。本文主要演示python中进行温度单位转换的代码过程,具体请看本文。一、问题温度有摄氏度(Celsius)和华氏度(Fabrenheit)两个不同的......

  3. 10步写了个Django网站

    Django做网站只要10步,真的只有10步,不信?咱们来数数……今天主要讲解用Pycharm编辑器搭建网站,网站功能是 实现在局域网中快速传递大文件! 比如:同事要给你个1G的文件,你丢一个网站链接给他。他上传后,文件就在你电脑啦!演示效果如下动图:从电脑F磁盘通过浏览器上传,在D盘出现文件,这里......

  4. Python学习(5)(while循环语句、循环嵌套、break/continue、赋值运算符、转义字符等 )

    Python学习(5)一、python的while 循环语句二、python的赋值运算符三、python的while 循环语句 示例四、python的 break 和 continue五、python的while 嵌套1、用嵌套打印小星星2、python 九九乘法表六、python的print 函数......

  5. Python+MySQL随机试卷及答案生成程序

    一、背景本文章主要是分享如何使用Python从MySQL数据库中面抽取试题,生成的试卷每一份都不一样。二、准备工作1.安装Python3下载地址:https://www.python.org/downloads/windows/2.安装库pip install python-docx==0.8.......

  6. 【Python爬虫】:使用高性能异步多进程爬虫获取豆瓣电影Top250

    在本篇博文当中,将会教会大家如何使用高性能爬虫,快速爬取并解析页面当中的信息。一般情况下,如果我们请求网页的次数太多,每次都要发出一次请求,进行串行执行的话,那么请求将会占用我们大量的时间,这样得不偿失。因此我们可以i使用高性能爬虫,也就是采用多进程,异步的方式对数据进行爬取和解析,这样就可以在更快......

  7. 如何使用Python进行PDF图片识别OCR

    使用场景 使用图片识别可以快速提取图片中的信息,方便高效。 Python并不能直接对PDF进行识别,所以如果是识别PDF的话,需要先将PDF转化为图片,然后再进行识别。 必备工具 Python 可以安装3.7及以上版本 tesseract-ocr......

  8. python中pyqtgraph知识点总结

    PyQtGraph是纯Python的,只是它底层调用的是PyQt,也就是Qt的Python封装,底层用C/C++语言开发的库,它是在PyQt的基础上开发的可视化控件,相较于老牌的绘制库,PyQtGraph在绘图速度上特别突出,保证绘图的美观性以及强大功能前提下,能够极高的效率去绘制图形,下面一起来详......

  9. Python3利用scapy局域网实现自动多线程arp扫描功能

    一、所需Python库from scapy.all import *import threading二、实现ip扫描1.获取c段ip地址在ARP()里面有ip地址,我们可以从里面提取出前3段出来ARP().show()然后通过从后查找最后一个.得到最后一段位数,然后总长度-最后一段长度就能取出前3段......

  10. python,selenium爬取微博热搜存入Mysql

    python爬取微博热搜存入Mysql 最终的效果 使用的库 目标分析 一:得到数据 二:链接......

随机推荐

  1. PHP 非常实用下载远程图片

    /*** 下载远程图片* @param string $url 图片的绝对url* @param string $filepath 文件的完整路径(例如/wwwhttp://www.580doc.com/images/test) ,此函数会自动根据图片url和http头信息确定图片的后缀名* @param string $filename ......

  2. Java 集合学习笔记

    Java集合自学学习笔记,内容主要包含:Collection接口及其子接口 List、Set,Iterator 迭代器接口,Map接口核Collections 工具类。@目录Java 集合(容器)一、Java 集合框架概述二、Collection 接口方法三、Iterator 迭代器接口1. 使用 ......

  3. 用python-webdriver实现自动填表的示例代码

    在日常工作中常常需要重复填写某些表单,如果人工完成,费时费力,而且网络延迟令人十分崩溃。如果能够用程序实现自动填表,效率可以提高一倍以上,并且能够移植到多台计算机,进一步提高工作效率。webdriver是python的selenium库中的一个自动化测试工具,它能完全模拟浏览器的操作,无需处理复杂的......

  4. Asp.Net 加密解密

    #region DES加密解密 /// /// DES加密 /// /// 待加密字串 /// 32位Key值 /// 加密后的字符串 public string DESEncrypt(string strSource){ return DESEncrypt(strSource, DESKey......

  5. python pillow库的基础使用教程

    知识点图像模块 (Image.Image)Image模块的功能Image模块的方法ImageChops模块ImageColor模块基础使用图像模块 Image.Image加载图像对象,旋转90度并显示from PIL import Image#显示图像im = Image.open('backgro......

  6. java中Map、Set、List的简单使用教程(快速入门)

    Map、Set、ListList的常用方法1、创建List list = new ArrayList<>(); List list = new LinkedList<>(); //同时可以作为链表用List> list = new ArrayList<>()......

  7. Winform 窗体自适应

    前言在使用 Winform 开发过程中,经常发些因为显示器分辨率、窗体大小改变,控件却不能自适应变化,几经查找资料,和大佬的代码。经过细小修改,终于可以让窗体在外界影响下,窗体内背景图片、控件都会自适应变化大小(类似于网页的响应式)。代码完整代码如下:using System;using Syste......

  8. ASP下通过Adodb.Stream实现多线程下载大文件

    有个朋友 做 某种小众音乐交换站的(他们那个行业的昵图网),需要用到付费下载。尝试过 防盗链,不太理想,最终使用了 Adodb.Stream 读取,直接输出。解决了 盗版的问题,但是新的问题又来了。Adodb.Stream 这种方式 电脑还好说,大部分电脑浏览器都支持。移动端 很多 浏览器为了 加速......

  9. C# 实现在当前目录基础上找到上一层目录

    其实很简单也很无脑,但却很实用,就是使用拆字符串的方法:/// /// 获得项目的根路径/// /// public string GetProjectRootPath(){string rootPath = "";string BaseDirectoryPath = AppDo......

  10. MySQL 5.6.35 索引优化导致的死锁案例解析

    本文描述了在一次压测过程中,由于Index Merge优化导致的死锁,详细描述了死锁产生的原因以及解决方案,并顺便介绍了Mysql索引结构及加锁机制。通过本文,大家可以掌握死锁分析的基本理论和一般方法,为工作中快速解决实际出现的死锁提供思路。一、背景随着公司业务的发展,商品库存从商品中心独立出来成为......