基于属性的测试

我慢慢滴慢慢滴学习到hypothesis是多么的聪明,但在一个非常基础的水平上,它允许你做的是,使用一个测试,以及测试参数的整个范围来取代测试一个单一的例子。

这里是一个简单的例子,基于我在2016年在Edinburgh的合作研讨会所给出的一个简单的演示。 你可以在这个链接vknight.org/Talks/2016-03-22-Division-by-11-and-property-based-testing-with-hypothesis/中找到我为其提供的幻灯片。

让我们编写一个测试是否能够被11整除的函数(假设我们并不知道有% 11这个东西。要做到这点,我们将使用下面的属性):

一个数可以被11整除,当前仅当该数字的数位(变换符号)之和为0.

例如:121可以被11整除,因为(1-2+1=0)(边注:如果你要在评论中对我嚷嚷,请阅读这篇文章的剩余部分)。

让我们将下面的代码保存在一个名为main.py的文件中:

def divisible_by_11(number):"""Uses above criterion to check if number is divisible by 11"""string_number = str(number)alternating_sum = sum([(-1) ** i * int(d) for i, din enumerate(string_number)])return alternating_sum == 0

现在,如果我们写了一些例子,那么我们可以得到期望输出:

>>> import main
>>> for k in range(10):
...     print(main.divisible_by_11(11 * k))
True
True
True
True
True
True
True
True
True
True

但是,让我们编写一个实际的测试套件。这是一个非常基本的单元测试,我们把它保存在test_main.py中:

import unittest
import main
​
class TestDivisible(unittest.TestCase):def test_divisible_by_11(self):
​for k in range(10):self.assertTrue(main.divisible_by_11(11 * k))self.assertFalse(main.divisible_by_11(11 * k + 1))
​# Some more examplesself.assertTrue(main.divisible_by_11(121))self.assertTrue(main.divisible_by_11(12122))
​self.assertFalse(main.divisible_by_11(123))self.assertFalse(main.divisible_by_11(12123))

上面的代码测试了可以被11整除的前10个数字(而这些数字+1不是),以及一些特定的测试(121和12123)。

然后,我们通过使用下面的命令运行它:

$ python -m unittest test_main
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
​
OK

现在,我们可以非常开心,并对自己感到非常自豪:我们已经测试了编写良好的软件,它可以传播以及被世界各地的研究者用来测试一个数字能否被11整除了!!

This is how mathematics breaks.

让我们编写一个hypothesis测试。我们将在test_property_main.py中编写下述代码:

import unittest
import main
​
from hypothesis import given  # This is how we will define inputs
from hypothesis.strategies import integers  # This is the type of input we will use
​
class TestDivisible(unittest.TestCase):
​@given(k=integers(min_value=1))  # This is the main decoratordef test_divisible_by_11(self, k):self.assertTrue(main.divisible_by_11(11 * k))

上面的代码其实是一个比之前更简单的测试:我们只是测试一个我们知道可以被11正常的数字,事实上从我们的函数中获得了期望的输出。

让我们运行它:

$ python -m unittest test_propert_main
Falsifying example: test_divisible_by_11(self=<test_property_main.TestDivisible testMethod=test_divisible_by_11>, k=19)
F
======================================================================
FAIL: test_divisible_by_11 (test_property_main.TestDivisible)
----------------------------------------------------------------------
Traceback (most recent call last):File "test_property_main.py", line 10, in test_divisible_by_11def test_divisible_by_11(self, k):File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 502, in wrapped_testprint_example=True, is_final=TrueFile "/usr/local/lib/python2.7/site-packages/hypothesis/executors.py", line 57, in default_new_style_executorreturn function(data)File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 103, in runreturn test(*args, **kwargs)File "test_property_main.py", line 11, in test_divisible_by_11self.assertTrue(main.divisible_by_11(11 * k))
AssertionError: False is not true
​
----------------------------------------------------------------------
Ran 1 test in 0.058s
​
FAILED (failures=1)

我们得到了一个错误!在顶部右侧,我们得到了Falsifying example提示,由此可见,对于k=19,我们的函数失败了。对于k=19,被测试的数字是(19*11=209)。这个数字显然可以被11整除(根据构造),但是它的交替和实际上是11,而不是0。

在这一点上,如前面关于被11整除所描述的,对于被11整除这个特性,我忽略了正确的特性:

一个数可以被11整除,当前仅当该数字的数位(变换符号)之和可以被11整除。

(在较老的博文中,有一个关于它的简单代数证明。)

现在,让我们修改我们的main.py (请注意,比我在研讨会上给出的那个随便的演示,这里我使用了一个稍微好点的修改):

def divisible_by_11(number):"""Uses above criterion to check if number is divisible by 11"""string_number = str(number)# Using abs as the order of the alternating sum doesn't matter.alternating_sum = abs(sum([(-1) ** i * int(d) for i, din enumerate(string_number)]))# Recursively calling the functionreturn (alternating_sum in [0, 11]) or divisible_by_11(alternating_sum)

运行测试:

$ python -m unittest test_propert_main
.
----------------------------------------------------------------------
Ran 1 test in 0.043s
​
OK

正如我们希望的!

我认为上面就是关于为什么基于属性的测试和工具,例如hypothesis,对于研究软件是有用的一个很好的例子。

它帮助我识别我的数学思维过程中的错误,一个我实际上认为已经很好的测试的错误(使用test_main.py测试用例)。如果不是因为hypothesis,我可能已经传播了这个重新定义什么是被11整除的代码。

如果你来只是想要获取hypothesis的一个基本工作样例,那么现在你可以停下来了 :)

显然,上面是一个简单的例子,但是在Axelrod (一个python包,用于再现迭代囚徒困境比赛)中,hypothesis最近被发现了一个bug。

Axelrod从123个策略(在撰写本文时)中挑取一个来创造球员锦标赛。下面是其中一个测试,它随机选取策略来创建锦标赛,然后检查其运行:

    @given(s=lists(sampled_from(axelrod.strategies),min_size=2,  # Errors are returned if less than 2 strategiesmax_size=5, unique=True),turns=integers(min_value=2, max_value=50),repetitions=integers(min_value=2, max_value=4),rm=random_module())@settings(max_examples=50, timeout=0)@example(s=test_strategies, turns=test_turns, repetitions=test_repetitions,rm=random.seed(0))def test_property_serial_play(self, s, turns, repetitions, rm):"""Test serial play using hypothesis"""# Test that we get an instance of ResultSet
​players = [strat() for strat in s]
​tournament = axelrod.Tournament(name=self.test_name,players=players,game=self.game,turns=turns,repetitions=repetitions)results = tournament.play()self.assertIsInstance(results, axelrod.ResultSet)self.assertEqual(len(results.cooperation), len(players))self.assertEqual(results.nplayers, len(players))self.assertEqual(results.players, players)

没有去到具体细节,在某一点上,hypothesis发现,两个特定的策略不能很好的一起工作。这里是github上的记录这个的issue。他们是:

  • Backstabber

  • MindReader

这两个策略事实上已经在一个锦标赛中一起使用了。包括所有的作弊策略的主要的锦标赛,而其副作用是另一个作弊策略先使用BackStabber,然后覆盖它的策略(Ele注:我也晕了,将就着看吧。原文是“The main tournament that includes all cheating strategies, but the side effect of that is that another cheating strategy played BackStabber first and overwrite it's strategy”)(意味着它可以很好地与MindReader发挥作用)。然而,hypothesis创建了一个比赛,其中,它们是完全对立的,然后bug就发生了。

反正,现在这个问题修复了,并且测试包含了一个@example语句来确保这个特定的错误确实修复了:你可以在源代码中看到这点。

在实践中,建议你使用基于属性的测试和传统的基于实例的测试的组合(因为有一些特定的东西需要检查),但是我认为,所有的测试套件通过使用像hypothesis这样的工具都可以得到提高,特别是研究软件。使用研究软件,一个强大的测试框架不仅有助于测试代码,还有助于测试想法。

有用的连接:

  • @DRMacIver: David是Hypothesis的作者。

  • Github仓库.

  • 文档.

最后,如果你使用IRC,那么我真心推荐在freenode上顺便看看#hypothesis。这里有一个直接链接到irccloud的通道:www.irccloud.com/invite?channel=%23hypothesis&amp;hostname=irc.freenode.net&port=6697&ssl=1。那里的每个人都是乐于助人的。

想获取python学习资料的小伙伴可以加QQ:728711576

基于属性的测试,hypothesis以及查找bug相关推荐

  1. 基于python渗透测试_Python中基于属性的测试简介

    基于python渗透测试 by Shashi Kumar Raja 由Shashi Kumar Raja Python中基于属性的测试简介 (Intro to property-based testi ...

  2. 赞扬别人团建评论_赞扬精心设计:基于属性的测试如何帮助我成为更好的开发人员...

    赞扬别人团建评论 开发人员的测试工具箱就是其中之一,很少保持不变. 可以肯定的是,一些测试实践已被证明比其他测试更有价值,但是,我们仍在不断寻找更好,更快和更具表现力的方法来测试我们的代码. 基于属性 ...

  3. 赞扬精心设计:基于属性的测试如何帮助我成为更好的开发人员

    开发人员的测试工具箱就是其中之一,很少保持不变. 可以肯定的是,某些测试实践已被证明比其他测试更有价值,但是,我们仍在不断寻找更好,更快和更具表现力的方法来测试我们的代码. 基于属性的测试 是 Jav ...

  4. 通过JavaScript中基于属性的TDD的钻石方块

    在上一篇文章中,我介绍了基于属性的测试背后的基本思想. 在这里,我将使用该技术来TDD钻石kata. 这篇文章受到很大的启发(即公然复制). 因此,请务必对Nat Pryce和Mark Seemann ...

  5. spring 中使用tdd_通过JavaScript中基于属性的TDD的钻石方块

    spring 中使用tdd 在上一篇文章中,我介绍了基于属性的测试背后的基本思想. 在这里,我将使用该技术来TDD钻石kata. 这篇文章受到很大启发(即公然复制). 因此,请务必对Nat Pryce ...

  6. 如何定位在测试中遇到的Bug?

    bug的分析和定位,这个话题是测试面试中经常聊到的,很多新手或者是日常工作中自我总结比较少的朋友,被问到的时候可能一下就懵了,今天分享一个完整清晰的思路给大家. 日常工作中,每天可能都会遇到不同的bu ...

  7. jax-rs jax-ws_信守承诺:针对JAX-RS API的基于合同的测试

    jax-rs jax-ws 自从我们谈论测试和应用有效的TDD做法以来,已经有一段时间了,特别是与REST(ful) Web服务和API有关的做法. 但是,这个主题永远都不应忘记,特别是在每个人都在做 ...

  8. 信守承诺:JAX-RS API的基于合同的测试

    自从我们谈论测试和应用有效的TDD实践以来已经有一段时间了,特别是与REST(ful) Web服务和API相关的实践. 但是,这个主题永远都不应忘记,特别是在每个人都在做微服务的世界中,无论它意味着什 ...

  9. 什么是基于模型的测试?

    基于模型的测试属于软件测试领域的一种测试方法.MBT步骤如下: 常规测试一般是由人来设计几个特定的测试场景,然后断言测试结果.测试用例固定.缺少不确定性.基于模型的测试(Model-based Tes ...

最新文章

  1. 如何估算各种物体的重心
  2. Elastic-Job发展历史
  3. Direct IO的程序实现
  4. aop日志(记录方法调用日志)
  5. ArcGIS REST API获取切片进度
  6. 20190429 - 如何访问 macOS 的 httpd、mysql 等服务
  7. java中的url进行编码和解码
  8. UE4保存信息到本地
  9. 大数据之Hadoop3.x模板虚拟机配置图解
  10. 软考网络工程师考试网络协议
  11. 2节串联锂电池充电管理IC芯片,5V,12V升降压解决方案
  12. Windows Mobile DIY ROM 常用工具
  13. PAT 乙级 集体照
  14. 诗词大全给力版_小学生诗词必背75+80首,课内课外全掌握,还送配套练习册amp;视频课...
  15. 建筑工程测量与测绘毕业论文范文
  16. CVPR'22 | 基于可形变关键点模型的图像驱动技术
  17. 十年育林,百度NLP已枝繁叶茂
  18. Linux下CGroup进行CPU内存等资源控制
  19. 【整理】22款免费在线图表制作工具
  20. Linux网络配置后无法正常上网

热门文章

  1. Web前端开发工程师--面试要求
  2. 光纤收发器结构介绍和故障解决
  3. 华为路由器交换机快捷键大全
  4. 渗透测试漏洞平台DVWA-参考答案
  5. 【题解】2020年蓝桥杯C/C++程序设计B组·试题 D: REPEAT 程序
  6. 树莓派连接不上WIFi,VNC失效,SSH失效
  7. Java8新特性(一)—————Lambda表达式
  8. 非企用户该如何推广?什么事非企户呢?
  9. 伽罗华域(Galois Field)理解、基于伽罗华域的四则运算(附详细python代码)
  10. lighting接口说明