优美的测试代码 - 行为驱动开发(BDD)

可理解的代码非常重要,测试代码也是如此。在我看来,优秀的测试代码,必须做到一个重要的事情就是保持测试逻辑的清晰。一个完整的测试案例通常包括三个部分:
1. SetUp
2. Exercise
3. Verifiy
4. TearDown
一 个测试案例如果能清晰的区分这三个部分,其实已经成功了一半。但是,如果仅仅只是做到这一步,离我们的“可理解的”测试代码还有些距离。在我看来,要做到 测试代码的可理解,首先要做到的是使用简洁清晰的方式表达每个测试案例的测试过程。我们都不希望在一个测试案例中看到一个long long method,因为我们需要去理解,你调用了那么多方法,做了那么多处理,到底是在干啥?当然,我们也不希望看到一个极端情况:一个测试案例中就看到一个 函数调用。这样做确实做到了测试案例表面上的简洁,但是,却失去了表现内部细节的机会。我们需要表述的是测试过程和方法,比如,进行了什么样的操作,输入 了什么样的数据,然后得出了什么样的结果,和预期结果对比怎么样等。如果把这些都封装到一个方法,别人只能理解你可能会做什么,但很难去理解你将如何去 做。其次,在测试案例fail的时候,能够清晰准确的定位问题。极限情况下,一个测试案例里应该只能有一个断言,或者说一个检查点。测试粒度的缩小能够加 快问题的定位。通常,当一个测试案例fail的时候,我们还需要定位到底是测试代码的问题还是被测代码的问题。这是一个烦人的过程,定位问题的人不一定就 是写这段测试代码的人,就算是写这段测试代码的人,也需要时间去理解测试案例干了什么。这时,测试过程及方法的清晰表述就变得尤为重要。
有的人会 想到通过文档来表述测试过程和方法,在我看来,这不是一个好办法。敏捷开发中的一个思想:“一个过时的文档比没有文档更加糟糕”。为什么需要文档才能表述清楚你的测试过程? 只能说明你的测试代码足够糟糕,无法让人理解。一个糟糕的代码就算当初写下了足够详细的说明文档,随着时间的交替,人员的变更,代码的修改维护,复杂的文 档和代码会变得越来越不同步。导致的结果将会是代码越来越糟糕复杂,文档越来越过时。最好的文档,其实是代码。
我们先来看一下一个老外写的完整的测试案例代码:
@Test
public void shouldBeAbleToEditAPage() {
    Given.thatThe(wiki).wasAbleTo(beAtThe(PointWhereItHasBeen.JUST_INSTALLED));
    And.thatThe(user).wasAbleTo(navigateToTheHomePage());

    When.the(user).attemptsTo(changeTheContent().to(
"Welcome to Acceptance Test Driven Development"));

    Then.the(textOnTheScreen().ofThe(user)).shouldBe(
"Welcome to Acceptance Test Driven Development");
}

我 之前并没有为这个测试案例做任何说明,相信每个看过这个测试案例的人都能非常容易的理解这个测试案例的行为,甚至是对代码不通的人。因为上面的测试代码使 用了几乎是自然语言的方式,描述了其测试的过程。了解的人一定看出来了,其实,这就是行为驱动开发,Behavior Driven Development,简称:BDD。


行为驱动开发(Behavior Driven Development)

Behaviour-Driven Development (BDD) is an evolution in the thinking behind TestDrivenDevelopment and AcceptanceTestDrivenPlanning.

It brings together strands from TestDrivenDevelopment and DomainDrivenDesign into an integrated whole, making the relationship between these two powerful approaches to software development more evident.

It aims to help focus development on the delivery of prioritised, verifiable business value by providing a common vocabulary (also referred to as a UbiquitousLanguage) that spans the divide between Business and Technology.
BDD 使用几乎近于自然语言的方式描述了软件的行为过程,因此,可以直接作为软件的需求文档,也可以直接应用到测试中,作为测试的标准文档。我们在做单元测试 时,经常是针对某个函数,或是某个类进行测试,但是被测函数或是被测的类是可能经常变化的,我们的测试案例也需要经常性的随之变化。然后,BDD描述的是软件的整个系统行为,几近于需求文档,可变性大大减小。因此,测试案例不需要做太大变化。同时,这样的测试案例最贴近于需求,贴近于实际的系统行为。
BDD描述的行为就像一个个的故事(Story),系统业务专家、开发者、测试人员一起合作,分析软件的需求,然后将这些需求写成一个个的故事。开发者负责填充这些故事的内容,测试者负责检验这些故事的结果。通常,会使用一个故事的模板来对故事进行描述:
As a [X]
I want [Y]
so that [Z]

同样的一个故事,可能会有不同的场景。通过上面的模板描述了故事之后,再通过下面的模板对不同场景进行描述:
Given some initial context (the givens),
Whenan eventoccurs,
then ensure some outcomes.

一个经典的例子就是ATM取款机的例子。故事的描述为:
Title: Customer withdraws cash
As a customer,
I want to withdraw cash from an ATM,
so that I don’t have to wait inline at the bank.

作为一个客户,我去ATM取钱,就不需要去排队。同样的故事,会有不同的场景发生:
Scenario 1: Account is incredit
Giventhe account is incredit
Andthe card isvalid
Andthe dispenser contains cash
Whenthe customer requests cash
Thenensure the account isdebited
Andensure cash isdispensed
Andensure the card isreturned

如果你取款的金额比你的存款还多,将是下面的场景:
Scenario 2: Account isoverdrawn past the overdraft limit
Giventhe account isoverdrawn
Andthe card isvalid
Whenthe customer requests cash
Thenensure a rejection message isdisplayed
Andensure cash isnot dispensed
Andensure the card isreturned

有了这样的故事、场景的描述,测试者可以通过一些BDD的测试框架将上面的故事转成测试代码(当然,也可直接由开发来完成,这里说测试者是为了方便理解),开发者实现产品代码,并保证测试代码通过。


常见的BDD框架

  • BOO - Specter: A tool written for the Boo language, a .Net and Mono programming language.

  • C - CSpec

  • C++ - CppSpec Spec-CPP

  • C# .Net - NSpec

  • .Net - NBehave

  • .Net - NSpecify (incomplete site)

  • Delphi - dSpec

  • Groovy - GSpec, http://easyb.org easyb, tspec a non-English BDD framework with Thai syntax.

  • Java - JBehave, JDave, beanSpec, Instinct

  • Javascript - JSSpec

  • PHP - PHPSpec

  • Python - Specipy, spec plugin for nose

  • Ruby - RSpec, Shoulda, test-spec & bacon, Cucumber

  • Scala - Specs


回到正题

刚才就像对一门新的技术或方向进行了了解,再回过头来想想,BDD对于测试的意义到底在哪。通过上面的了解,我们知道了行为驱动开发并不止是一个测试行为,其实很大意义上是一个产品需求分析人员、开发、测试共同来完成的一个行为。BDD同时也是一个非常理想化的过程,几乎现在大部分的公司连TDD都实现不 了。作为测试人员,我们不是要去抱怨,我们应该庆幸作为测试开发人员,我们有机会使用最前沿,最先进的技术去解决问题。开发不能做到TDD,我们可以对自己的测试代码实施TDD,我们可以尝试各种不同的语言,然后选择最优雅的方式去实现。同样的,我们可以使用BDD所使用的自然语言描述方法来编写我们的测试案例。这其实才是我本文的关键所在。
使用行为驱动开发,还需要打破传统的魄力。因为之前几乎没有人会告诉你一个函数的命名为ShouldXXX(),也不会有有When, Then,And之类的类或函数。当你习惯它,会变得非常好玩。
使用行为驱动开发,可以使我们的案例描述逻辑更加清晰,可以想象以后维护你的代码的人会对你大赞一番,因为省去了他理解代码的大部分时间。
使用行为驱动开发,可以建立自动化测试与手工系统测试的桥梁。或许可以形成这样的模式:手工测试人员负责设计故事和场景,自动化测试人员负责实现故事和场景。通过这样的联系,手工测试人员能够更好的了解到自动化测试所做的内容,从而免去不必要的重复劳动。
使用行为驱动开发,可以使你的测试更加贴近实际的用户行为,从而找到系统的问题所在。


没有银弹

没有银弹》(No Silver Bullet)是IBM大型电脑之父佛瑞德·布鲁克斯(Fred Brooks)在1987年所发表的一篇关于软件工程的 经典论文。该论述中强调由于软件的复杂性本质,而使真正的银弹并不存在;所谓的没有银弹是指没有任何一项技术或方法可使软件工程的生产力在十年内提高十 倍。刚才提到的使用行为驱动开发的种种好处,但是,如果运用不当,往往会适得其反。我们使用When,Then之类的连词作为函数,并不是说可以随意根据 需要随意的命名。比如,使用This或These作为函数名,其行为就应该是返回一个或多个对象,而不是做其他的事情。可以想象,如果一个函数的名称是 These,里面执行的确实一个文件拷贝的操作,对于代码的维护者来说是多么糟糕的一件事情啊。
其实我对行为驱动开发也只是初略的了解,本文表达的是仅仅是我个人的一些思想,如有错误之处也希望大家能够指出。最后,我希望达到的是:

优美的测试代码,就是一个个优美的故事。

本文参考资料:
http://behaviour-driven.org/
http://dannorth.net/introducing-bdd
http://www.javaworld.com/javaworld/jw-09-2008/jw-09-easyb.html

转载于:https://www.cnblogs.com/coderzh/archive/2009/07/26/1531633.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/304651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#连接MySQL数据库实例

项目目的:连接mysql查询数据并将数据显示到界面的datagridview里面.Step1:添加动态链接库文件Visual Studio,在 项目(右键)-管理NuGet程序包(N) 然后在浏览里面搜索MySql.Data并进行安装。Step2:using所需要的库using MySql.Data.MySqlClient;step3&…

java字符串比大小_Java字符串比较(3种方法)

字符串比较是常见的操作,包括比较相等、比较大小、比较前缀和后缀串等。在 Java 中,比较字符串的常用方法有 3 个:equals() 方法、equalsIgnoreCase() 方法、 compareTo() 方法。下面详细介绍这 3 个方法的使用。equals() 方法equals() 方法将…

链接服务器 慢_redis服务器cpu100%的原因和解决方案

首先引起cpu100%可能的几大原因:1.redis连接数过高2.数据持久化导致的阻塞3.主从存在频繁全量同步4.value值过大5.redis慢查询为了模拟redis服务器cpu100%,临时买了一台阿里云ecs,并把那天清空前的redis备份还原到服务器上。下面我们按照顺序…

有人问我:AI这么火,要不要去追赶AI的热潮?

12月14日,吴恩达发布微博:我很高兴地宣布Landing.ai的成立,开始进入AI产业。 作为一家人工智能(AI)公司,Landing.ai旨在帮助企业在人工智能时代实现转型。这一动作让人们对AI的关注度在2017年的末尾又一次升…

Exchange2003管理

邮件的管理详细见邮件转载于:https://blog.51cto.com/ruzhulinux/187799

过完年,又不想上班了?

大家新年好,我是Z哥。每次过完年,不少人会多增加一份焦虑,这份焦虑表面上看是“不想上班”,但实际上可能是职业规划的缺失导致。因为缺少对未来的预期、憧憬,导致对自己的工作没有热情。这种情况的另一种叫法是「职业倦…

15天助你掌握问卷统计与Spss实战

最近总是有小伙伴咨询超模君关于数据分析的问题,比如数据分析是什么、怎样才能速成数据分析等等,今天超模君就抽个空跟大家谈谈数据分析那些事儿。首先,我们先来了解了解小伙伴所提到的数据分析。数据分析是指用适当的统计分析方法对收集来的…

aswing学习笔记3-在JPanel中,如何将.png格式的图片设置为背景?

在JPanel中,如何将.png格式的图片设置为背景?2009-03-04 19:21在JPanel中,如何将.png格式的图片设置为背景? 发表于 : 周三 6月 04, 2008 3:53 pm由 tvrcaiyy在JPanel中,如何将.png格式的图片设置为背景?能…

在.NET Core 中使用 FluentValidation 进行规则验证

不用说,规则验证很重要,无效的参数,可能会导致程序的异常。如果使用Web API或MVC页面,那么可能习惯了自带的规则验证,我们的控制器很干净:public class User {[Required]public string FirstName { get; se…

scrcpy投屏_安卓投屏利器——PC一键控制多台手机

点击关注,我们共同每天进步一点点!之前给大家介绍了投屏开源工具scrcpy(Scrcpy投屏,在电脑上流畅操控你的手机!),今天要介绍的投屏工具是在scrcpy的基础上进行了二次开发,使用更加友好。《安卓投屏》基于Gi…

java emoji编码转换_java转换emoji表情

/*** Description 将字符串中的emoji表情转换成可以在utf-8字符集数据库中保存的格式(表情占4个字节,需要utf8mb4字符集)* param str* 待转换字符串* return 转换后字符串* throws UnsupportedEncodingException* exception*/public static String emojiConvert1(St…

【干货】通俗理解神经网络中激活函数作用

推荐阅读时间8min~13min主要内容:通俗理解激活函数,主要来自我在学习会的slides,讲解了激活函数的非线性能力和组合特征的作用下面我分别对激活函数的两个作用进行解释。1加入非线性因素,解决非线性问题好吧,很容易能够…

创建第一个WCF程序

WCF的三大核心是ABC A代表Address-where(对象在哪里) B代表Binding-how(通过什么协议取得对象) C代表Contact(契约)-what(定义的对象是什么,如何操纵) 创建一个空的解决方…

.NET微服务最佳实践eShopOnContainers

本文翻译自微软Docs, 内嵌译者多年使用的参悟,如理解有误,请不吝赐教。微软与社区专家合作,开发了功能齐全的云原生微服务示例应用eShopOnContainers。该应用旨在展示使用.NET、Docker以及可选的Azure,Kubernetes技术来…

爬取猎聘python_爬取猎聘大数据岗位相关信息--Python

猎聘网站搜索大数据关键字,只能显示100页,爬取这一百页的相关信息,以便做分析。__author__ ‘Fred Zhao‘import requestsfrom bs4 import BeautifulSoupimport osimport csvclass JobSearch():def __init__(self):self.headers {‘User-Ag…

java string底层实现_Java-学习日记(Shell与String底层原理)

Java杂记-2020.08.07Test中测试所有getter,setter方法最近一周在写codereview,相关技术是kmock1.0.19,gradlebuild.gradle中配置文件testCompile pl.pojo:pojo-tester:0.7.6testCompile(junit:junit:4.12)testCompile(kmock:kmock:1.0.19)Test中的使用&a…

正态分布为什么常见?

统计学里面,正态分布(normal distribution)最常见。男女身高、寿命、血压、考试成绩、测量误差等等,都属于正态分布。>>>> 作者: 阮一峰以前,我认为中间状态是事物的常态,过高和过低…

Hibernate HQL 语法大全(上)

Hibernate配备了一种非常强大的查询语言,这种语言看上去很像SQL。但是不要被语法结构上的相似所迷惑,HQL是非常有意识的被设计为完全面向对象的查询,它可以理解如继承、多态 和关联之类的概念。 1.大小写敏感性问题 除了Java类与属性的名称外…

python tuple args_Python基本数据类型之tuple

一、创建元组:ages (11, 22, 33, 44, 55)ages tuple((11, 22, 33, 44, 55))元组和列表几乎一样元组的元素不可修改,但是元组元素的元素是可以修改的tuple(iterable),可以存放所有可迭代的数据类型二、基本操作:索引name_tuple …

轻量级Excel读取器ExcelReader

用户上传Excel文件,要求读取里面数据,其实可以很简单!新版Excel2007以上的xlsx文件,本质上是OpenXml格式,只需要解开压缩包然后读取内部Xml即可得到想要的数据。ExcelReader 用于快速读取单Sheet的Excel数据&#xff0…