优美的测试代码 - 行为驱动开发(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 whol
e, 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 co
mmon vocabulary (also referred to as a UbiquitousLanguage) that spans the divide between Business and
Technology.
BDD 使用几乎近于自然语言的方式描述了软件的行为过程,因此,可以直接作为软件的需求文档,也可以
直接应用到测试中,作为测试的标准文档。我们在做单元测试 时,经常是针对某个函数,或是某个类进行
测试,但是被测函数或是被测的类是可能经常变化的,我们的测试案例也需要经常性的随之变化。然后,
BDD描述的是软件的整个系统行为,几近于需求文档,可变性大大减小。因此,测试案例不需要做太大变
化。同时,这样的测试案例最贴近于需求,贴近于实际的系统行为。
BDD描述的行为就像一个个的故事(Story),系统业务专家、开发者、测试人员一起合作,分析软件的需求,
然后将这些需求写成一个个的故事。开发者负责填充这些故事的内容,测试者负责检验这些故事的结果。
通常,会使用一个故事的模板来对故事进行描述:
As a
I want
so that
同样的一个故事,可能会有不同的场景。通过上面的模板描述了故事之后,再通过下面的模板对不同场
景进行描述:
Given some initial context (the givens),
When an event occurs,
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 in line at the bank.
作为一个客户,我去ATM取钱,就不需要去排队。同样的故事,会有不同的场景发生:
复制代码
Scenario 1: Account is in credit
Given the account is in credit
And the card is valid
And the dispenser contains cash
When the customer requests cash
Then ensure the account is debited
And ensure cash is dispensed
And ensure the card is returned
复制代码
如果你取款的金额比你的存款还多,将是下面的场景:
复制代码
Scenario 2: Account is overdrawn past the overdraft limit
Given the account is overdrawn
And the card is valid
When the customer requests cash
Then ensure a rejection message is displayed
And ensure cash is not dispensed
And ensure the card is returned
复制代码
有了这样的故事、场景的描述,测试者可以通过一些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同时也是一个非常理想化的过程,几乎现在大部分的公司连TD
D都实现不 了。作为测试人员,我们不是要去抱怨,我们应该庆幸作为测试开发人员,我们有机会使用
最前沿,最先进的技术去解决问题。开发不能做到TDD,我们可以对自己的测试代码实施TDD,我们可
以尝试各种不同的语言,然后选择最优雅的方式去实现。同样的,我们可以使用BDD所使用的自然语言
描述方法来编写我们的测试案例。这其实才是我本文的关键所在。
使用行为驱动开发,还需要打破传统的魄力。因为之前几乎没有人会告诉你一个函数的命名为ShouldXXX
(),也不会有有When, Then,And之类的类或函数。当你习惯它,会变得非常好玩。
使用行为驱动开发,可以使我们的案例描述逻辑更加清晰,可以想象以后维护你的代码的人会对你大赞一
番,因为省去了他理解代码的大部分时间。
使用行为驱动开发,可以建立自动化测试与手工系统测试的桥梁。或许可以形成这样的模式:手工测试
人员负责设计故事和场景,自动化测试人员负责实现故事和场景。通过这样的联系,手工测试人员能够
更好的了解到自动化测试所做的内容,从而免去不必要的重复劳动。
使用行为驱动开发,可以使你的测试更加贴近实际的用户行为,从而找到系统的问题所在。
请问有详细一点的,结合案例的方案吗 支持分享
页:
[1]