为驱动开发(Behavior-Driven Development,BDD)是一种卓越的开发模式。能帮助开发者养成日清日结的好习惯,从而避免甚至杜绝"最后一分钟"的情况出现,因此对提高代码质量是大有裨益的。其与Gherkin语法相结合的测试结构及设计形式,使得对团队的全部成员包括非技术人员都具有极好的易读性。
所有代码都必须进行测试,这意味着上线时把系统瑕疵降到最低甚至为零。这需要与完整的测试套件相配,从整体把控软件行为,使得检测与维护都能有序进行。这就是BDD的魅力所在,难道不心动吗?
什么是BDD?
BDD的概念和理论源自TDD(测试驱动开发),类似于TDD的理论要点是在编码前先写好测试。不同点是除了使用单元测试进行细粒度化测试,还使用接受测试(acceptance tests)贯穿程序始末。接下来我们会结合Lettuce测试框架进行讲解。
BDD过程可简单概括为:
编写一个缺陷接受测试
在每个功能里,如有需要重复上述步骤。
敏捷开发中的BDD
在敏捷开发中,BDD更是如鱼得水。
如果项目的新功能和新需求每隔一、两个星期就发生变更,那么该团队需要配合进行快节奏的测试和编码工作。Python中的接受和单元测试可以帮助实现该目标。
接受测试为人熟知的是使用了一个英文格式的"特性"描述文件,内容是含有的测试以及个别测试。这样做的好处是使整个项目团队都参与其中,除了开发者,还有管理者与商业分析者等不参与实际测试过程的非技术成员。
特性文件的编写遵循全员可读的规则,使技术和非技术成员都能清楚理解与接收。如果只包含单元测试,那么有可能会导致需求分析不全面或不能达成共识。接受测试的最大优点是适用性强,不论项目规模大小都能运用自如。
Gherkin语法
通常会使用Gherkin来编写接受测试,Gherkin来自Cucumber框架,由Ruby语言所编写。Gherkin语法十分简单,在Lettuce Python中主要使用以下8点来进行特性和测试的定义:
Given假设
安装
使用Python常用的pip install语句就可完成Lettuce包的安装:
$ pip install lettuce
$ lettuce /path/to/example.feature用于运行测试。可以每次只运行一个测试文件,或者是提交目录名来运行目录下的所有文件。
为了使测试的编写和使用更加容易,我们建议把nosetests也安装好:
$ pip install nose
特性文件
特性文件由英语写成,内容是测试所覆盖的程序范围。此外还包括测试的创建任务。换言之,你除了需要编写测试,还得规范自己就程序的方方面面编写出良好的文档。这样做的好处是使自己对代码上下都心中有数,明确下一步做什么。随着项目规模的扩大,文档的重要性会逐步显现;例如重新回顾某个功能或对某个调用API进行回溯等等。
接下来会结合TDD中的一个实例创建一个特性文件。该实例是一个由Python写成的简易计算器,同时会演示接受测试的基本写法。目录构成的建议是建立两个文件夹,一个是app,用于放置代码文件如calculator.py;另一个是tests,用于放置特性文件夹。
calculator.py:
class Calculator(object):
def add(self, x, y):
number_types = (int, long, float, complex)
if isinstance(x, number_types) and isinstance(y, number_types):
return x + y
else:
raise ValueError
tests/features目录下的特性文件calculator.feature
Feature: As a writer for NetTuts
I wish to demonstrate
How easy writing Acceptance Tests
In Python really is.
Background:
Given I am using the calculator
Scenario: Calculate 2 plus 2 on our calculator
Given I input "2" add "2"
Then I should see "4"
从该例子不难看出特性文件的描述是非常直截了当的,能够使全体成员都能看明白。
特性文件的三个要点:
Feature block(特性区块):该处描述了测试组所涵盖的程序内容。这里不执行任何代码,但能使阅读者明白正要进行什么样的特性测试。
步骤(Steps)文件
除了特性文件,步骤文件也是必须的,这是"见证奇迹的时刻"。显然地,特性文件本身不会做出什么结果;它需要步骤文件依次地与Python执行代码一一映射才有最后的结果输出。这里应用的是正则表达式。
正则表达式?不会过于复杂吗?其实在BDD世界里,正则表达式常用于捕捉整个字符串或从某行抓取变量。所以熟能生巧。
正则表达式?在测试中使用不会太复杂吗?在Lettuce是不会的,反而是非常简单的。
以下是对应的步骤文件的编写:
from lettuce import *
from nose.tools import assert_equals
from app.calculator import Calculator
@step(u'I am using the calculator')
def select_calc(step):
print ('Attempting to use calculator...')
world.calc = Calculator()
@step(u'I input "([^"]*)" add "([^"]*)"')
def given_i_input_group1_add_group1(step, x, y):
world.result = world.calc.add(int(x), int(y))
@step(u'I should see "([^"]+)"')
def result(step, expected_result):
actual_result = world.result
assert_equals(int(expected_result), actual_result)
文件首部分是标准的导入写法。例如对Calculator的访问和Lettuce工具的导入,还有nosetest包中assert_equals断定方法的导入。接下来,你就可以开始针对特性文件的每一行进行步骤定义。如前所述,正则表达式很多时候用于提取整个字符串,除了有时需要在某行对变量进行访问。
在这个例子中, 里的@step起到解码提取的作用;u字母的意思是以unicode编码方式进行表达式执行。接着是使用正则表达式对引用的内容进行匹配,这里是要进行相加的数字。
然后是对Python方法传入变量,变量名可任意定义,这里使用x和y作为calculator add方法的传入变量名。
此外需要介绍world变量的使用。world是一个全局容器,使得变量可以在同一场景的不同步骤中使用。否则,所有变量只对应于其所在方法可用。例如把add方法的运算结果存放于某个step,而在另一外一个step进行结果的断定。
特性的执行
特性文件和步骤文件都完成后,接下来可以运行测试来看看能否通过。内建测试运行机的Lettuce执行方式是很简单的,例如
lettuce test/features/calculator.feature:
$ lettuce tests/features/calculator.feature
Feature: As a writer for NetTuts # tests/features/calculator.feature:1
I wish to demonstrate # tests/features/calculator.feature:2
How easy writing Acceptance Tests # tests/features/calculator.feature:3
In Python really is. # tests/features/calculator.feature:4
Background:
Given I am using the calculator # tests/features/steps.py:6
Given I am using the calculator # tests/features/steps.py:6
Scenario: Calculate 2 plus 2 on our calculator # tests/features/calculator.feature:9
Given I input "2" add "2" # tests/features/steps.py:11
Then I should see "4" # tests/features/steps.py:16
1 feature (1 passed)
1 scenario (1 passed)
2 steps (2 passed)
Lettuce的输出是非常工整的,它清楚显示了哪行特性文件代码被执行了,然后对成功执行的行以高亮绿色显示。此外还显示了正在运行的特性文件以及行号,这对于测试失败时进行特性文件缺陷行的查找是很有帮助的。输出末尾是特性,场景,步骤的执行个数以及通过个数的结果汇总。本例中所有测试都通过了。但如果出现错误,Lettuce会如何处理呢?
首先得对calculator.py代码进行修改,把add方法改为两数相减:
class Calculator(object):
def add(self, x, y):
number_types = (int, long, float, complex)
if isinstance(x, number_types) and isinstance(y, number_types):
return x - y
else:
raise ValueError
再次运行,看看Lettuce是如何对错误进行说明的:
$ lettuce tests/features/calculator.feature
Feature: As a writer for NetTuts # tests/features/calculator.feature:1
I wish to demonstrate # tests/features/calculator.feature:2
How easy writing Acceptance Tests # tests/features/calculator.feature:3
In Python really is. # tests/features/calculator.feature:4
Background:
Given I am using the calculator # tests/features/steps.py:6
Given I am using the calculator # tests/features/steps.py:6
Scenario: Calculate 2 plus 2 on our calculator # tests/features/calculator.feature:9
Given I input "2" add "2" # tests/features/steps.py:11
Then I should see "4" # tests/features/steps.py:16
Traceback (most recent call last):
File "/Users/user/.virtualenvs/bdd-in-python/lib/python2.7/site-packages/lettuce/core.py", line 144, in __call__
ret = self.function(self.step, *args, **kw)
File "/Users/user/Documents/Articles - NetTuts/BDD_in_Python/tests/features/steps.py", line 18, in result
assert_equals(int(expected_result), actual_result)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 515, in assertEqual
assertion_func(first, second, msg=msg)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 508, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 4 != 0
1 feature (0 passed)
1 scenario (0 passed)
2 steps (1 failed, 1 passed)
List of failed scenarios:
Scenario: Calculate 2 plus 2 on our calculator # tests/features/calculator.feature:9
显然,实际结果0与预期结果4是不符的。Lettuce清楚显示了该问题,接下来就是调试排错直到通过的时间了。
其它工具
在Python中还提供了很多不同的工具来进行类似的测试,这些工具基本源自Cucumber。例如:
Behave:这是一个Cucumber接口。文档配套齐备,保持更新,有不少的配套工具。
不论使用什么工具,只要对某个工具运用熟练了,其它的自然能融会贯通。对教程文档的熟读是成功的第一步。
优点
自信地进行代码重构
使用一个完整测试套件的优点是显而易见的。找到一个强大的测试套件,会让代码重构工作事半功倍,信心满满。
随着项目规模的不断扩大,如果缺乏有效的工具,这不啻会使回溯和重构工作困难重重。如果有一套完整的接受测试来与每个特性一一对应,那么将能使变更工作有序不紊地进行,不会对现有功能模块造成破坏。
全员都能参与其中的接受测试,将能极大地提升团队战斗力,一开始就朝着同一目标前进。程序员可把精力用在精确的目标上,避免需求范围的失控;测试员可就特性文件进行一一检阅,把测试环节做到极致。最后形成良性循环,使得程序的每个特性都完美交付。
综述
结合上述过程和工具,在过往工作过的团队中我们都曾取得不错的成绩。BDD开发方式可使整个团队保持专注,保持自信,保持活力,并使潜在错误降到最低。
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。