在 测试金字塔 的底部是单元测试。单元测试每次只测试一个代码单元,通常是一个函数或方法。

通常,设计单个单元测试是为了测试通过一个函数或特定分支的特定执行流程,这使得将失败的单元测试和导致失败的 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 执行健壮的单元测试的更多相关文章

  1. pychrame更换默认以unittest执行或取消单元测试框架执行

    选择某个测试框架运行脚本 File-> Settings -> Tools -> Python Integrated Tools -> Default test runner ...

  2. Vue Cli 中使用 Karma / Chrome 执行样式相关单元测试

    在 GearCase 开源项目 中,我使用了 Vue Cli 的默认测试框架.因此和样式相关的东西,都无法进行测试.因为它并不类似于无头浏览器,而是存在于虚拟内存之中. 现状 在如下 button.s ...

  3. PHP单元测试使用

    单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类, ...

  4. Spring Boot从Controller层进行单元测试

    单元测试是程序员对代码的自测,一般公司都会严格要求单元测试,这是对自己代码的负责,也是对代码的敬畏. 一般单元测试都是测试Service层,下面我将演示从Controller层进行单元测试. 无参Co ...

  5. 8点了解Java服务端单元测试

    一. 前言 单元测试并不只是为了验证你当前所写的代码是否存在问题,更为重要的是它可以很大程度的保障日后因业务变更.修复Bug或重构等引起的代码变更而导致(或新增)的风险. 同时将单元测试提前到编写正式 ...

  6. React单元测试——十八般兵器齐上阵,环境构建篇

    一个完整.优秀的项目往往离不开单元测试的环节,就 github 上的主流前端项目而言,基本都有相应的单元测试模块. 就 React 的项目来说,一套完整的单元测试能在在后续迭代更新中回归错误时候给与警 ...

  7. C#单元测试面面观

    标题有点标题党,但相信各位看完这篇文章一定会所收获,如果之前没有接触过单元测试或了解不深通过本文都能对单元测试有个全新认识.本文的特点是不脱离实际,所测试的代码都是常见的模式. 写完这篇文章后,我看了 ...

  8. TDD学习笔记【二】---单元测试简介

    大纲 Testing 的第一个切入点:单元测试. 本篇文章将针对单元测试进行简介,主要内容包含了5W: Why What Where Who When 而How 的部分,属于实现部分,将于下一篇文章介 ...

  9. 使用Android Studio进行单元测试

    Android Studio默认支持Android单元测试,不需要像网上说的配置mainifest.xml或build.gradle. 创建单元测试文件夹 可以把单元测试文件夹放到你自己创建的文件夹中 ...

随机推荐

  1. nginx使用手册+基本原理+优缺点

    一.nginx优点 1.反向代理 1.正向代理: 客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原 ...

  2. Thread --三大特性 原子性,可见性,有序性

    原子性.可见性.有序性 什么是原子性 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行. 一个很经典的例子就是银行账户转账问题: 比如从账户A向账户B转1000元, ...

  3. MATLAB——元胞数组

    一. 1.元胞数组的创建 >> a={;ones(,),:} a = ] [2x3 ;ones(,),:} >> b=[{};{ones(,)},{:}] b = ] [2x3 ...

  4. CSS盒子模型(boeder)+浮动(float)+定位(position)

    盒子的上下层:margin--background-color--background-image--padding--content--border(最外层) 计算一个盒子宽 =  内容的宽(wid ...

  5. 学习方法,学习方式By:ラピスラズリ(Dawn)20200407

    原创,转载请注明,谢谢!

  6. 发现钉钉打卡定位算法的一个bug

    最近公司取消了指纹打卡,改用钉钉打卡. 天天用这个打卡上班,经常忘记,困扰. 最烦的是好几次明明人在办公室,打卡地址显示在10分钟前的位置,定位失败,不得不重新打卡. 经历过几次定位失败后,我就琢磨起 ...

  7. # H - H HDU - 2066 (多起点、多终点问题)

    H - H HDU - 2066 (多源点.多汇点问题) 一个图上,有M条边,Z个出发点,Y个终止点.求一条最短路,其中起点是Z中的任意一点,终点是Y中任意一点. Input 输入数据有多组,输入直到 ...

  8. 使用Markdown编辑总结

    Markdown是轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档. Markdown可以导出HTML .Word.图像.PDF.Epub 等多种格式的文档. 后缀为.md或者.markdo ...

  9. 1055 The World's Richest (25分)(水排序)

    Forbes magazine publishes every year its list of billionaires based on the annual ranking of the wor ...

  10. python_Mock基本使用

    ## 1.mock简介 1. py3已将mock集成到unittest库中 2. 为的就是更好的进行单元测试 3. 简单理解,模拟接口返回参数 4. 通俗易懂,直接修改接口返回参数的值 5. 官方文档 ...