店名测试打分免费测试?店名测试打分免费测试吉凶!

朋友们,我来了~

今天这一章还是讲测试,基于属性的测试(Property-Based Testing)。其实我感觉叫随机验证测试更通俗易懂一点。

我们日常自己测试的时候,会有一个问题,就是我们既是裁判,也是球员。因为我们的程序就是顺着我们的思路写的,测试也是按照同样的思路来测试的。所以,我们可能通常很难测出我们自己写出来的bug。

解决这个问题的一个办法,就是把测试交给其他人。这也是测试这个岗位存在的意义之一。但是,如果我们把测试交给了其他人,上一章提到的,对测试的思考可以帮助更好地设计程序,这个好处就不存在了。

基于属性测试

解决这个问题的办法就是,把其他人,换成我们的计算机,让计算机来帮我们自动测试。

之前关于契约或者合约(Contract)那一章中,提到了要给程序制定一个合同,规定了输入的规则、输出的规则、以及不变量(比如给一个数组排序,进去和出来的数组长度应该是不变的)。

那么合约和不变量在这里统称为属性(property),很抽象是不是?个人觉得它的定义并不重要,举个例子就懂了。

比如我们要验证一个list的排序,我们可以验证这两件事:1.输入和输出的list长度是否相等;2.是不是list里的每一个都比它前面一个大。

python写出来就是这样:

from hypothesis import given
import hypothesis.strategies as some

@given(some.lists(some.Integers()))
def test_list_size_is_invariant_across_sorting(a_list):
original_length = len(a_list)
a_list.sort()
assert len(a_list) == original_length

@given(some.lists(some.text()))
def test_sorted_result_is_ordered(a_list):
a_list.sort()
for i in range(len(a_list) – 1):
assert a_list[i] <= a_list[i + 1]

更重要的是,它利用了hypothesis这个模块,@given(some.lists(some.integers()))会让它在运行的时候,利用随机的数值把同样的方法运行100次。它会把出现错误的情况记录下来。

对数器

之前在学习算法的时候,也接触到了一个叫做对数器的概念,其实和这里的基于属性测试,基本上是一件事情。

就是为了验证我们的算法A写对了,我们先写一个绝对正确的算法B,不管效率,只管正确。然后用同样的数据同时跑算法A和算法B。当然也是随机跑很多次啦。如果两个出现了不同的结果,那就说明算法A写错了。

思路基本相同,只不过,相对来说,可能这个基于属性测试更严谨一点,比如,同样是排序算法,我用我写的冒泡排序算法来验证我的选择排序算法,万一,我的冒泡排序也写错了呢?但是,如果我直接从根本上解析出排序(正序)就是后一个比前一个大,显然是更不容易出问题的,其实也能更进一步地锻炼我们寻找根本问题的能力。

当然啦,严格意义上来说,比较后一个比前一个大,这个本身也是一种算法。

实话说,要思考清楚有哪些属性是要测试的,这件事本身就充满了难度。如果你平时没有这个习惯,突然让你想,你会大脑一片空白的,就像是刚学编程那会,遇到了一个需求完全不知道从哪里下手的那种感觉。坦白的说,我现在就是这种状态,想必这也是需要刻意练习的。

这种随机大量测试的方式,可以帮助我们测出一些边界值,测出一些我们想不到的情况。往往最容易出问题的地方也是在边界值的地方。就跟开车似的,车开起来了,通常都没什么问题,但是起步可能会熄火,停车可能会倒不进去。

Java的相关框架

关于这个基于属性测试的框架,我随手搜了一搜,Python有的,没理由咱们Java没有,对不对?

一、找到两个github上开源:

1.https://github.com/HypothesisWorks/hypothesis-java

2.https://github.com/quicktheories/QuickTheories

二、找到一个都已经有自己网站的(虽然也有github):

https://jqwik.net/

三、还有一个直接是JUnit家的JUnit-Quickcheck(感觉上这个更香一点)

https://github.com/pholser/junit-quickcheck

我还没来得及仔细研究,朋友们可以先自行研究起来~

另一个实例

书中其实还提到了另外一个更加实际一点的例子,但我个人觉得那算不上bug,它和实际的需求有关系。这边简单提一下吧,或许你也有不同的见解。

大概就是有一个仓库类,它可以存放各种货品,不同货品有各自的数量,大概是这样一个结构吧:List<Map<String,Integer>>。然后我们可以查询某个货品是否有库存、查询某个货品还剩多少库存、可以从中取出某个数量的货品。

代码如下:

class Warehouse:
def __init__(self, stock):
self.stock = stock

def in_stock(self, item_name):
return (item_name in self.stock) and (self.stock[item_name] > 0)

def take_from_stock(self, item_name, quantity):
if quantity <= self.stock[item_name]:
self.stock[item_name] -= quantity
else:
raise Exception("Oversold {}".format(item_name))

def stock_count(self, item_name):
return self.stock[item_name]

仓库的初始化是这样的:

wh = Warehouse({"shoes": 10, "hats": 2, "umbrellas": 0})

然后,同样用了hypothesis来做批量测试,然后在调用take_from_stock( item_name='hats', quantity=3)这样一组数据的时候报错了。

作者说,在in_stock我们不应该只判断库存是不是大于0,而是要判断它有没有包含我们要拿取的货品数。代码应该改成这样:

def in_stock(self, item_name, quantity):
return (item_name in self.stock) and (self.stock[item_name] >= quantity)

反正我是觉得这不算个bug吧,毕竟在真正获取货品的时候,就报错了呀。要看我们对于in_stock这个方法本身的要怎么定义咯,是只需要知道它还有没有库存,还是需要知道它有没有我需要的库存。

虽然实际需求中,后者可能性更大,但是在take_from_stock方法里报错,又有什么问题呢?(又或者,作者只是想举个例子,是我太较真了)

尾声

基于属性的测试是对于单元测试的补充,对于单元测试的思考,可以让我们思考代码实现的其他方式。基于属性的测试,可以让我们更加清晰,我们的方法能干什么不能干什么,同时,也消除一些意外的情况。

如果,你还没有把这两种测试用起来,现在就赶紧用起来吧~

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 673862431@qq.com 举报,一经查实,本站将立刻删除。
如若转载,请注明出处:https://www.bbqim8.com/archives/25944