使用 PyHamcrest 执行健壮的单元测试
在 测试金字塔 的底部是单元测试。单元测试每次只测试一个代码单元,通常是一个函数或方法。
通常,设计单个单元测试是为了测试通过一个函数或特定分支的特定执行流程,这使得将失败的单元测试和导致失败的 bug 对应起来变得容易。
理想情况下,单元测试很少使用或不使用外部资源,从而隔离它们并使它们更快。
单元测试套件通过在开发过程的早期发现问题来帮助维护高质量的产品。有效的单元测试可以在代码离开开发人员机器之前捕获 bug,或者至少可以在特定分支上的持续集成环境中捕获 bug。这标志着好的和坏的单元测试之间的区别:好的测试通过尽早捕获 bug 并使测试更快来提高开发人员的生产力。坏的测试降低了开发人员的工作效率。
当测试附带的特性时,生产率通常会降低。当代码更改时测试会失败,即使它仍然是正确的。发生这种情况是因为输出的不同,但在某种程度上是因为它不是 函数契约(function’s contract)的一部分。
因此,一个好的单元测试可以帮助执行函数所提交的契约。
如果单元测试中断,那意味着该契约被违反了,应该(通过更改文档和测试)明确修改,或者(通过修复代码并保持测试不变)来修复。
虽然将测试限制为只执行公共契约是一项需要学习的复杂技能,但有一些工具可以提供帮助。
其中一个工具是 Hamcrest ,这是一个用于编写断言的框架。最初是为基于 Java 的单元测试而发明的,但它现在支持多种语言,包括 Python 。
Hamcrest 旨在使测试断言更容易编写和更精确。
def add(a, b):
return a + b
from hamcrest import assert_that, equal_to
def test_add():
assert_that(add(2, 2), equal_to(4))
这是一个用于简单函数的断言。如果我们想要断言更复杂的函数怎么办?
def test_set_removal():
my_set = {1, 2, 3, 4}
my_set.remove(3)
assert_that(my_set, contains_inanyorder([1, 2, 4]))
assert_that(my_set, is_not(has_item(3)))
注意,我们可以简单地断言其结果是任何顺序的 1、2 和 4,因为集合不保证顺序。
我们也可以很容易用 is_not 来否定断言。这有助于我们编写精确的断言,使我们能够把自己限制在执行函数的公共契约方面。
然而,有时候,内置的功能都不是我们真正需要的。在这些情况下,Hamcrest 允许我们编写自己的 匹配器(matchers)。
想象一下以下功能:
def scale_one(a, b):
scale = random.randint(0, 5)
pick = random.choice([a,b])
return scale * pick
我们可以自信地断言其结果均匀地分配到至少一个输入。
匹配器继承自 hamcrest.core.base_matcher.BaseMatcher,重写两个方法:
class DivisibleBy(hamcrest.core.base_matcher.BaseMatcher):
def __init__(self, factor):
self.factor = factor
def _matches(self, item):
return (item % self.factor) == 0
def describe_to(self, description):
description.append_text('number divisible by')
description.append_text(repr(self.factor))
编写高质量的 describe_to 方法很重要,因为这是测试失败时显示的消息的一部分。
def divisible_by(num):
return DivisibleBy(num)
按照惯例,我们将匹配器包装在一个函数中。有时这给了我们进一步处理输入的机会,但在这种情况下,我们不需要进一步处理。
def test_scale():
result = scale_one(3, 7)
assert_that(result,
any_of(divisible_by(3),
divisible_by(7)))
请注意,我们将 divisible_by 匹配器与内置的 any_of 匹配器结合起来,以确保我们只测试函数提交的内容。
在编辑这篇文章时,我听到一个传言,取 “Hamcrest” 这个名字是因为它是 “matches” 字母组成的字谜。嗯…
>>> assert_that("matches", contains_inanyorder(*"hamcrest")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/moshez/src/devops-python/build/devops/lib/python3.6/site-packages/hamcrest/core/assert_that.py", line 43, in assert_that
_assert_match(actual=arg1, matcher=arg2, reason=arg3)
File "/home/moshez/src/devops-python/build/devops/lib/python3.6/site-packages/hamcrest/core/assert_that.py", line 57, in _assert_match
raise AssertionError(description)
AssertionError:
Expected: a sequence over ['h', 'a', 'm', 'c', 'r', 'e', 's', 't'] in any order
but: no item matches: 'r' in ['m', 'a', 't', 'c', 'h', 'e', 's']
经过进一步的研究,我找到了传言的来源:它是 “matchers” 字母组成的字谜。
>>> assert_that("matchers", contains_inanyorder(*"hamcrest"))
>>>
如果你还没有为你的 Python 代码编写单元测试,那么现在是开始的好时机。如果你正在为你的 Python 代码编写单元测试,那么使用 Hamcrest 将允许你使你的断言更加精确,既不会比你想要测试的多也不会少。这将在修改代码时减少误报,并减少修改工作代码的测试所花费的时间。
使用 PyHamcrest 执行健壮的单元测试的更多相关文章
- pychrame更换默认以unittest执行或取消单元测试框架执行
选择某个测试框架运行脚本 File-> Settings -> Tools -> Python Integrated Tools -> Default test runner ...
- Vue Cli 中使用 Karma / Chrome 执行样式相关单元测试
在 GearCase 开源项目 中,我使用了 Vue Cli 的默认测试框架.因此和样式相关的东西,都无法进行测试.因为它并不类似于无头浏览器,而是存在于虚拟内存之中. 现状 在如下 button.s ...
- PHP单元测试使用
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类, ...
- Spring Boot从Controller层进行单元测试
单元测试是程序员对代码的自测,一般公司都会严格要求单元测试,这是对自己代码的负责,也是对代码的敬畏. 一般单元测试都是测试Service层,下面我将演示从Controller层进行单元测试. 无参Co ...
- 8点了解Java服务端单元测试
一. 前言 单元测试并不只是为了验证你当前所写的代码是否存在问题,更为重要的是它可以很大程度的保障日后因业务变更.修复Bug或重构等引起的代码变更而导致(或新增)的风险. 同时将单元测试提前到编写正式 ...
- React单元测试——十八般兵器齐上阵,环境构建篇
一个完整.优秀的项目往往离不开单元测试的环节,就 github 上的主流前端项目而言,基本都有相应的单元测试模块. 就 React 的项目来说,一套完整的单元测试能在在后续迭代更新中回归错误时候给与警 ...
- C#单元测试面面观
标题有点标题党,但相信各位看完这篇文章一定会所收获,如果之前没有接触过单元测试或了解不深通过本文都能对单元测试有个全新认识.本文的特点是不脱离实际,所测试的代码都是常见的模式. 写完这篇文章后,我看了 ...
- TDD学习笔记【二】---单元测试简介
大纲 Testing 的第一个切入点:单元测试. 本篇文章将针对单元测试进行简介,主要内容包含了5W: Why What Where Who When 而How 的部分,属于实现部分,将于下一篇文章介 ...
- 使用Android Studio进行单元测试
Android Studio默认支持Android单元测试,不需要像网上说的配置mainifest.xml或build.gradle. 创建单元测试文件夹 可以把单元测试文件夹放到你自己创建的文件夹中 ...
随机推荐
- coding++:MD5加密(JAVA加密 与 JS加密不一致问题)
要求:根据指定 字符加密 JS中的加密方法 要和 JAVA中的算法保持一致,解决如下: var rotateLeft = function (lValue, iShiftBits) { retur ...
- iOS 架构
一.MVC MVC 全名 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离 ...
- [JZOJ5343]健美猫<模拟>
[思路] 这个是一个非常容易看出来的模拟,但是模拟也是有技巧的 一般人的模拟思路一般就是移动元素或者下标 然后我就看到了一个有趣的思路 建立坐标轴 以i坐标为横坐标,以si为纵坐标,然后画一条斜率为1 ...
- [Asp.Net Core] 为什么选择 Blazor Server Side (一) 快速实现图片验证码
关于Blazor 由于在国内, Blazor一点都不普及, 建议读者翻看我之前写的随笔, 了解Blazor Server Side的特点. 在一段时间内, 我会写一些解说分析型的 "为什么选 ...
- 【cs224w】Lecture 1 & 2 - 图的性质 及 随机图
目录 Lecture 1: Introduction Lecture 2: Properties and Random Graph Degree Distribution Path Length Cl ...
- 如何让Java应用成为杀不死的小强?(中篇)
各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 上期我们抛了一个砖:“如何实现 Java 应用进程的状态监控,如果被监控的进程 down 掉,是否有机制能启动起来?” ...
- 001_Chrome 76支持原生HTML 图片懒加载Lazy loading
Table Of Content 什么是懒加载? 语法参数及使用方式? 有哪些特点? 与js有关的实践 什么是懒加载? 技术背景 Web应用需要经常向后台服务器请求资源(通过查询数据库,是非常耗时耗资 ...
- pip 命令参数以及如何配置国内镜像源
文章更新于:2020-04-05 注:如果 pip 命令不可以用,参见:python pip命令不能用 文章目录 一.参数详解 1.命令列表 2.通用参数列表 二.实际应用 1.常用命令 2.`pip ...
- 细数Java项目中用过的配置文件(YAML篇)
灵魂拷问:YAML,在项目中用过没?它与 properties 文件啥区别? 目前 SpringBoot.SpringCloud.Docker 等各大项目.各大组件,在使用过程中几乎都能看到 YAML ...
- MTK Android Driver :Lcm
MTK Android Driver :lcm 1.怎样新建一个LCD驱动 LCD模组主要包括LCD显示屏和驱动IC.比如LF040DNYB16a模组的驱动IC型号为NT35510.要在MTK6577 ...