- 浏览: 3507787 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
wanglf1207:
EJB的确是个不错的产品,只是因为用起来有点门槛,招来太多人吐 ...
weblogic-ejb-jar.xml的元素解析 -
qwfys200:
总结的不错。
Spring Web Flow 2.0 入门 -
u011577913:
u011577913 写道也能给我发一份翻译文档? 邮件437 ...
Hazelcast 参考文档-4 -
u011577913:
也能给我发一份翻译文档?
Hazelcast 参考文档-4 -
songzj001:
DbUnit入门实战
开始学习使用 JBehave
测试驱动的开发(TDD)在实践中是一个很好的思想,但有些开发人员还不能接受 “测试” 这个词所产生的概念上的骤变。在本文中,学习一种更自然的方法,将 TDD 元素整合到编程实践中。开始采用行为驱动开发(BDD)(通过 JBehave),亲身体验将注意力集中在程序行为(而不是输出)时获得的效果。
显然,测试本身是件好事。而在早期进行测试 — 例如在编写代码时 — 则更有益处,这特别有利于提高代码质量。在开发早期编写测试,您将获益良多。您能够检查代码的行为,并预先对它进行调试,这种动力无疑是巨大的。
即使了解了这种重要性,我们也没有达到关键的一点:使在编写代码之前 编写测试成为一种标准实践。正如 TDD 是极限编程(Extreme Programming)的下一个演化阶段(后者推出了单元测试框架),以 TDD 为基础,新的飞跃也将到来。本月,我邀请您和我一起实现从 TDD 到更具直观性的行为驱动测试(BDD)的演化。
|
虽然测试优先编程对于有些人比较管用,但是并不适用于每一个人。虽然有的应用程序开发人员狂热拥护 TDD,但也有人坚决抵制它。即使现在已经有了很多测试框架,例如 TestNG、 Selenium 和 FEST,但不对 代码进行测试的理由仍然充分。
不 采用 TDD 的两个常见理由是 “没有足够的时间进行测试” 和 “代码太复杂,难以测试”。测试优先编程的另一个障碍是测试优先概念本身。很多人把测试看作一种反应型活动,仅比抽象具体一点。经验告诉我们,不能测试不 存在的东西。对于某些开发人员来说,对于这种概念框架,测试优先 是一种矛盾的说法。
但是,如果不考虑编写测试和如何测试,而是考虑行为 ,结果会如何呢?这里所说的行为,是指一个应用程序应该 如何运行 — 实际上就是指它的规范。
实际上,您已经想到了这种方法。我们都想到过。请看下面的对话。
Frank: 什么是栈?
Linda: 它是一种数据结构,按先进后出(或后进先出)的方式收集对象。它通常有一个 API,其中包括push()
和pop()
等方法。有时也有peek()
方法。
Frank:push()
有什么功能?
Linda:push()
接受一个输入对象,比如说foo
,并将它放入到一个内部容器(例如一个数组)中。push()
通常不返回结果。
Frank: 如果我push()
两个对象,比如先是foo
,然后是bar
,结果会怎样?
Linda: 第二个对象bar
应该在栈(至少包含两个对象)的顶部,所以如果调用pop()
,那么返回的应该是bar
,而不是foo
。如果再次调用pop()
,那么应该返回foo
,然后栈为空(假设在添加这两个对象之前栈中没有对象)。
Frank: 也就是说,pop
移除最近放入栈中的项目?
Linda: 是的,pop()
应该移除最上面的项目(假设栈中还有可移除的项目)。peek()
与此类似,只是不移除栈中的对象。peek()
应该保留栈顶的项目。
Frank: 如果之前没有 push 任何项目,那么调用pop()
时会怎样?
Linda:pop()
应该抛出一个异常,表明栈中尚未 push 任何项。
Frank: 如果push()
null
会怎样?
Linda: 栈应该抛出一个异常,因为null
不是一个有效的可push()
的值。
在这段对话中,有没有注意到什么特别的地方呢(除了 Frank 不是计算机科学专业的)?这里从头到尾没有用到 “测试” 这个词。但是,“应该” 这个词却非常自然地随处闪现。
|
BDD 并不是什么新生事物,更不具备什么革命性的突破。它只是 TDD 的一个分支,其中 “测试” 这个词换成了 “应该”。除了语义,很多人还发现,与测试 概念相比,应该 这个概念是一种更自然的开发驱动因素。考虑行为(应该)会自然而然地促使您先编写规范类,而后者可以成为一个非常有效的实现驱动因素。
以 Frank 和 Linda 的对话为基础,让我们看看 BDD 如何以 TDD 希望推广的方式驱动开发。
|
|
JBehave 是用于 Java™ 平台的一个 BDD 框架,源于 xUnit 范例。正如您所料,JBehave 强调应该 这个词,而不是测试 。和 JUnit 一样,您可以在自己喜欢的 IDE 中,或者通过偏爱的构建平台(例如 Ant)运行 JBehave 类。
JBehave 允许以 JUnit 的方式创建行为类;但是,在 JBehave 中,不需要扩展任何特定的基类,并且所有行为方法都需要以 should
而不是 test
开头,如清单 1 所示。
public class StackBehavior { public void shouldThrowExceptionUponNullPush() throws Exception{} public void shouldThrowExceptionUponPopWithoutPush() throws Exception{} public void shouldPopPushedValue() throws Exception{} public void shouldPopSecondPushedValueFirst() throws Exception{} public void shouldLeaveValueOnStackAfterPeep() throws Exception{} } |
清单 1 中定义的方法都是以应该
开头,它们都创建一个人类可读的句子。这里产生的 StackBehavior
类描述 Frank 和 Linda 之间的对话中提到的栈的很多特性。
例如,Linda 说,如果用户试图将 null
放到栈上,那么栈应该
抛出一个异常。查看 StackBehavior
类中的第一个行为方法:该方法的方法名为 shouldThrowExceptionUponNullPush()
。其它方法的命名也遵从这一模式。这种描述性命名模式(这并不是 JBehave 或 BDD 特有的)便于以人类可读的方式报告失败行为,您很快就可以看到这一点。
说到 shouldThrowExceptionUponNullPush()
,那么如何验证这个行为呢?似乎 Stack
类首先需要有一个 push()
方法,这很容易定义。
public class Stack<E> { public void push(E value) {} } |
可以看到,我编写了一个最简单的栈,以便首先
添加必需的行为。正如 Linda 所说,行为很简单:如果有人对 null
值调用 push()
,那么栈应该
抛出一个异常。现在看看我在清单 3 中如何定义这个行为。
清单 3. 如果推出一个 null 值,则栈应该抛出一个异常
public void shouldThrowExceptionUponNullPush() throws Exception{ final Stack<String> stStack = new Stack<String>(); Ensure.throwsException(RuntimeException.class, new Block(){ public void run() throws Exception { stStack.push(null); } }); } |
|
|
在清单 3 中发生的一些事情是 JBhave 特有的,所以要解释一下。首先,我创建 Stack
类的一个实例,并将它限制为 String
类型(通过 Java 5 泛型)。接下来,我使用 JBehave 的
异常框架
实际建模我所期望的行为。
Ensure
类类似于 JUnit 或 TestNG 的
Assert
类型;但是,它增加了一系列方法,提供了更具可读性的 API(这常被称作文学编程
)。在清单 3 中,我确保了如果对 null
调用 push()
,则抛出一个 RuntimeException
。
JBehave 还引入了一个 Block
类型,它是通过用所需的行为覆盖 run()
方法来实现的。在内部,JBehave 确保期望的异常类型不被抛出(并因此被捕捉),而是生成一个故障状态。您可能还记得,在我前面关于 用 Google Web Toolkit 对
Ajax 进行单元测试
的文章中,也出现了类似的覆盖便利类的模式。在那种情况下,覆盖是通过
GWT 的 Timer
类实现的。
如果现在运行清单 3 中的行为,应该看到出现错误。按照目前编写的代码,push()
方法不执行任何操作。所以不可能生成异常,从清单 4 中的输出可以看到这一点。
1) StackBehavior should throw exception upon null push: VerificationException: Expected: object not null but got: null: |
清单 4 中的句子 “StackBehavior should throw exception
upon null push
” 模拟行为的名称(shouldThrowExceptionUponNullPush()
),并加上类的名称。
实际上,JBehave 是在报告当它运行所需的行为时,没有获得任何反应。当然,我的下一步是要使上述行为成功运行,为此我检查 null
,如清单 5 所示。
public void push(E value) { if(value == null){ throw new RuntimeException("Can't push null"); } } |
当我重新运行行为时,一切都运行得很好,如清单 6 所示。
Time: 0.021s Total: 1. Success! |
清单 6 中的输出与 JUnit 的输出是不是很像?这也许不是巧合,对不对?如前所述,JBehave 是根据 xUnit 范例建模的,它甚至通过 setUp()
和 tearDown()
提供了对 fixture 的支持。由于我可能在整个行为类中使用一个 Stack
实例,我可能也会将那种逻辑推入(这里并非有意使用双关语)到一个 fixture 中,正如清单 7 中那样。注意,
JBehave 将与 JUnit 一样遵循相同的 fixture 规则 — 也就是说,对于每个行为方法,它都运行一个 setUp()
和 tearDown()
。
public class StackBehavior { private Stack<String> stStack; public void setUp() { this.stStack = new Stack<String>(); } //... } |
对于接下来的行为方法,shouldThrowExceptionUponPopWithoutPush()
表示我必须确保它具有类似于 清单
3
中的 shouldThrowExceptionUponNullPush()
的行为。从清单 8 中可以看出,没有任何特别神奇的地方
— 有吗?
public void shouldThrowExceptionUponPopWithoutPush() throws Exception{ Ensure.throwsException(RuntimeException.class, new Block() { public void run() throws Exception { stStack.pop(); } }); } |
您可能已经清楚地知道,此时清单 8 并不会真正地编译,因为 pop()
还没有被编写。但是,在开始编写 pop()
之前,让我们考虑一些事情。
从技术上讲,在这里我可以将 pop()
实现为无论调用顺序如何,都只抛出一个异常。但是当我沿着这条行为路线前进时,我又忍不住考虑一个支持我所需要的规范的实现。在这种情况下,如果 push()
没有被调用(或者从逻辑上讲,栈为空)的情况下确保 pop()
抛出一个异常,则意味着栈有一个状态。正如之前 Linda 思考的那样,栈通常有一个 “内部容器”,用于实际持有项目。相应地,我可以为 Stack
类创建一个 ArrayList
,用于保持传递给 push()
方法的值,如清单 9 所示。
public class Stack<E> { private ArrayList<E> list; public Stack() { this.list = new ArrayList<E>(); } //... } |
现在我可以为 pop()
方法编写行为,即确保当栈在逻辑上为空时,抛出一个异常。
public E pop() { if(this.list.size() > 0){ return null; }else{ throw new RuntimeException("nothing to pop"); } } |
当我运行清单 8 中的行为时,一切如预期运行:由于栈中没有存在任何值(因此它的大小不大于 0),于是抛出一个异常。
接下来的行为方法是 shouldPopPushedValue()
,这个行为方法很容易指定。我只是 push()
一个值(“test”
),并确保当调用 pop()
时,返回相同的值。
清单 11. 如果将一个值入栈,那么出栈的也应该是它,对吗?
public void shouldPopPushedValue() throws Exception{ stStack.push("test"); Ensure.that(stStack.pop(), m.is("test")); } |
|
|
|
在清单 11 中,我确保 pop()
返回值 “test”
。在使用 JBehave 的 Ensure
类的过程中,您常常会发现,需要一种更丰富的方式来表达期望。JBehave 提供了一种 Matcher
类型用于实现丰富的期望,从而满足了这一需求。而我选择重用 JBehave 的 UsingMatchers
类型(清单 11 中的 m
变量),所以可以使用 is()
、and()
、or()
等方法和很多其它整洁的机制来构建更具文学性的期望。
清单 11 中的 m
变量是
StackBehavior
类的一个静态成员,如清单 12 所示。
private static final UsingMatchers m = new UsingMatchers(){}; |
有了清单 11 中编写的新的行为方法之后,现在可以来运行它 — 但是这时会产生一个错误,如清单 13 所示。
Failures: 1. 1) StackBehavior should pop pushed value: java.lang.RuntimeException: nothing to pop |
怎么回事?原来是我的 push()
方法还没有完工。回到 清单 5
,我编写了一个最简单的实现,以使我的行为可以运行。现在是时候完成这项工作了,即真正将被推入的值添加到内部容器中(如果这个值不为 null
)。如清单 14 所示。
public void push(E value) { if(value == null){ throw new RuntimeException("Can't push null"); }else{ this.list.add(value); } } |
但是,等一下 — 当我重新运行该行为时,它仍然失败!
清单 15. JBehave 报告一个 null 值,而不是一个异常
1) StackBehavior should pop pushed value: VerificationException: Expected: same instance as <test> but got: null: |
至少清单 15 中的失败有别于清单 13 中的失败。在这种情况下,不是抛出一个异常,而是没有发现 "test"
值;实际弹出的是 null
。仔细观察 清单 10
会发现:一开始我将 pop()
方法编写为当内部容器中有项目时,就返回 null
。问题很容易修复。
public E pop() { if(this.list.size() > 0){ return this.list.remove(this.list.size()); }else{ throw new RuntimeException("nothing to pop"); } } |
但是,如果现在我重新运行该行为,我又收到一个新的错误。
1) StackBehavior should pop pushed value: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1 |
仔细阅读清单 17 中的实现可以发现问题:在处理 ArrayList
时,我需要考虑 0
。
public E pop() { if(this.list.size() > 0){ return this.list.remove(this.list.size()-1); }else{ throw new RuntimeException("Nothing to pop"); } } |
|
|
至此,通过允许传递多个行为方法,我已经实现了 push()
和 pop()
方法。但是我还没有处理栈的实际内容,这是与多个 push()
和 pop()
相关联的逻辑,间或出现一个 peek()
。
首先,我将通过 shouldPopSecondPushedValueFirst()
行为确保栈的基本算法(先进先出)无误。
public void shouldPopSecondPushedValueFirst() throws Exception{ stStack.push("test 1"); stStack.push("test 2"); Ensure.that(stStack.pop(), m.is("test 2")); } |
清单 19 中的代码可以按计划运行,所以我将实现另一个行为方法(在清单 20 中),以确保两次使用 pop()
都能表现出正确的行为。
public void shouldPopValuesInReverseOrder() throws Exception{ stStack.push("test 1"); stStack.push("test 2"); Ensure.that(stStack.pop(), m.is("test 2")); Ensure.that(stStack.pop(), m.is("test 1")); } |
接下来,我要确保 peek()
能按预期运行。正如 Linda 所说,peek()
遵从和 pop()
相同的规则,但是 “应该保留栈顶的项目”。相应地,我在清单 21 中实现了 shouldLeaveValueOnStackAfterPeep()
方法的行为。
public void shouldLeaveValueOnStackAfterPeep() throws Exception{ stStack.push("test 1"); stStack.push("test 2"); Ensure.that(stStack.peek(), m.is("test 2")); Ensure.that(stStack.pop(), m.is("test 2")); } |
由于 peek()
还没有定义,因此清单 21 还不能编译。在清单 22 中,我定义了 peek()
的一个最简单的实现。
public E peek() { return null; } |
现在 StackBehavior
类可以编译,但是它仍然不能运行。
1) StackBehavior should leave value on stack after peep: VerificationException: Expected: same instance as <test 2> but got: null: |
在逻辑上,peek()
不会从内部集合中移除
项目,它只是传递指向那个项目的指针。因此,我将对 ArrayList
使用
get()
方法,而不是 remove()
方法,如清单 24 所示。
public E peek() { return this.list.get(this.list.size()-1); } |
现在重新运行 清单 21
中的行为,结果顺利通过。但是,在这样做的过程中发现一个问题:如果栈为空,则 peek()
有怎样的行为?如果说栈为空时调用 pop()
会抛出一个异常,那么 peek()
是否也应该如此?
Linda 对此没有进行解释,所以,显然我需要自己添加新的行为。在清单 25 中,我为 “当之前没有调用 push()
时调用 peek()
会怎样” 这个场景编写了代码。
清单 25. 如果没有调用 push 就调用 peek,会怎样?
public void shouldReturnNullOnPeekWithoutPush() throws Exception{ Ensure.that(stStack.peek(), m.is(null)); } |
同样,不会感到意外。如清单 26 所示,问题出现了。
1) StackBehavior should return null on peek without push: java.lang.ArrayIndexOutOfBoundsException: -1 |
修复这个缺陷的逻辑类似于 pop()
的逻辑,如清单 27 所示。
public E peek() { if(this.list.size() > 0){ return this.list.get(this.list.size()-1); }else{ return null; } } |
把我对 Stack
类作出的所有修改和修复综合起来,可以得到清单 28 中的代码。
import java.util.ArrayList; public class Stack<E> { private ArrayList<E> list; public Stack() { this.list = new ArrayList<E>(); } public void push(E value) { if(value == null){ throw new RuntimeException("Can't push null"); }else{ this.list.add(value); } } public E pop() { if(this.list.size() > 0){ return this.list.remove(this.list.size()-1); }else{ throw new RuntimeException("Nothing to pop"); } } public E peek() { if(this.list.size() > 0){ return this.list.get(this.list.size()-1); }else{ return null; } } } |
在此,StackBehavior
类运行 7 种行为,以确保 Stack
类能按照
Linda 的(和我自己的一点)规范运行。Stack
类
还可能使用某种重构(也许 pop()
方法
应该调用 peek()
进行测试,而不是执行 size()
检查?),但是由于一直使用了行为驱动过程,我可以很自信地对代码作出更改。如果出现了问题,很快就可以收到通知。
|
|
您 可能已经注意到,本月对行为驱动开发(BDD)的探索中,Linda 实际上就是客户。在这里,可以把 Frank 看作开发人员。如果把这里的领域(即数据结构)换成其它领域(例如一个呼叫中心应用程序),以上应用仍然类似。作为客户或领域专家的 Linda 指出系统、特性或应用程序应该 执行什么功能,像 Frank 这样的开发人员则使用 BDD 确保正确理解了她的要求并实现这些需求。
对于很多开发人员来说,从测试驱动开发转移到 BDD 是明智的转变。 如果采用 BDD,就不必考虑测试,而只需注意应用程序的需求,并确保应用程序的行为执行它 应该 执行的功能,以满足那些需求。
在这个例子中,使用 BDD 和 JBehave 使我可以根据 Linda 的说明轻松地实现一个可正常工作的栈。通过首先 考虑行为,我只需倾听她的需求,然后相应地构建栈。在此过程中,我还发现了 Linda 没有提及的关于栈的其他内容。
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文
。
-
“追求代码质量:对 Ajax 应用程序进行单元测试
”(Andrew Glover,developerWorks,2007 年 7 月):通过使用 GWT 和它的重写类
Timer
,测试 Ajax 应用程序变得更容易。
-
“追求代码质量: 使用 Selenium 和 TestNG 进行编程式测试
”(Andrew Glover,
developerWorks,2007 年 4 月):学习如何使用 TestNG 作为测试驱动器,通过编程的方式运行 Selenium 测试。
-
“使用 RSpec 进行行为驱动测试
”(Bruce Tate,developerWorks,2007 年 8 月):在过去一年里,测试领域中最为瞩目的创新应属 RSpec 的引入和快速发展,它是一种行为驱动测试工具。了解 RSpec 如何改变人们思考测试的方式。
-
“Introducing BDD
”(Dan North,
DanNorth.net,2006 年 9 月):了解 Dan North 如何将 BDD 作为一种实践。
-
“Using BDD
to drive development
”(Andrew Glover,testearly.com,2007 年 7 月):Andrew 再次介绍 BDD 如何驱动开发,同样也是基于 JBehave。
-
“Mocks
are hip when it comes to BDD
”(Andrew Glover,thediscoblog.com,2007 年 7 月):Andrew 通过 JBehave 的 mocking 库重新发现 mock 对象,然后他使用这种对象驱动快速开发。
-
追求代码质量
系列
(Andrew Glover,developerWorks):学习更多关于编写专注于质量的代码的信息。
-
developerWorks Java 技术专区
:这里有数百篇关于 Java 编程方方面面的文章。
获得产品和技术
下载 JBehave
:面向 Java 平台的完全启用的BDD框架。
发表评论
-
Fitnesse使用
2012-05-05 13:27 23508Fitnesse 的使用 一,介绍 Fitnesse是一种 ... -
Customizing the new FitNesse parser
2012-05-05 13:13 2141FitNesse began its life using ... -
单元测试------理论篇
2011-03-12 12:20 1662测试是软件开发的重要 ... -
DbUnit入门实战
2011-03-09 09:13 32720相信做过单元测试的 ... -
一个愚蠢农夫和奶牛的故事-转载
2011-03-07 09:20 1778Ivar Jacobson博士 ... -
Web测试工具Selenium入门心得
2011-02-16 23:50 34842009-02-05 ... -
selenium 和webdriver_入门实践
2011-02-16 23:47 8980我们一直非常强调建立以底层为核心的分层自动化测试 ... -
VPS服务器性能压力测试工具(转载)
2010-12-01 23:35 3278VPS服务器性能 压力测试工具 http_load、webb ... -
软件测试工具英雄榜
2010-08-24 11:12 5039几乎毫无悬念地, ... -
净室软件认证
2010-05-16 22:20 1596软件测试 的统计方 ... -
SVN—patch的应用
2010-03-07 14:43 27461.create patch 使用create ... -
diff和patch十分钟指南
2010-03-07 14:10 2701情景一:你正尝试从代码编译一个软件包,发现有人已经对代码进行了 ... -
patch用法(转)
2010-03-07 14:08 9187首先介绍一下 diff 和 patch 。 ... -
补丁Patch
2010-03-07 14:04 3135补丁Patch是天才程序员、Perl的发明者Larry ... -
开源性能测试工具Curl-Loader
2010-02-28 14:52 4867curl-loader 是一个用C语言 编写的Web 应用 ... -
用 easyb 驱动开发
2009-01-03 22:25 2228长期以来,定义需求 ... -
软件测试之测试策略
2009-01-03 22:05 3492第一部分 软件测试 策略基础 为什么要编写测试策 ... -
自动化测试框架模型SAFS
2009-01-03 22:00 2612SAFS(Software A ... -
探索 JUnit 4.4 新特性
2008-12-22 18:34 2116随着当前 Java 开发的越发成熟,Agile 和 T ... -
Hamcrest指南
2008-12-22 18:22 26926Hamcrest 官方网站 Hamcrest Tutor ...
相关推荐
通过分析和修改这些代码,读者可以亲身体验到敏捷开发方法论的实际应用,提升自己的编程技能。 5. **设计模式**:PPP书籍中涵盖了一些经典的设计模式,如工厂模式、单例模式、观察者模式、装饰器模式等。这些模式是...
4. 提供演示或试用:展示产品或服务的功能和价值,让客户亲身体验。 5. 销售谈判:针对客户需求进行价格和条款的协商,达成共识。 6. 关闭交易:完成销售流程,签署合同,正式成为客户。 7. 后续服务:提供优质的...
本书的最后一部分通过一个或多个完整的实战项目,让读者亲身体验整个开发流程。这些项目涵盖了各种常见的应用场景和技术难点,旨在帮助读者巩固所学知识,并能够在实际工作中灵活运用。 1. **项目规划**:明确项目...
4. **现场演示**:在上海站还将设有现场虚拟驾驶系统及控制模块演示、交互式硬件在环演示等环节,让参会者亲身体验基于模型开发的实际效果。 #### 五、结论 Altair基于模型的开发及其应用研讨会在多座城市举办,...
3. **测试驱动开发(TDD)**:TDD是一种编程实践,要求先编写测试用例,再编写满足这些测试的代码。在教学中,可以引导学生理解测试的重要性,提高代码质量。 4. **重构**:重构是改进代码结构而不改变其外在行为的...
在实施“行为化”教学时,首先要求学生具备一定的单片机基础知识,然后通过组装智能小车并编写控制程序,让学生亲身体验C语言的实用性。例如,项目1让学生组装智能小车,并设计让小车按指定路径行走的程序,这涉及到...
在MATLAB开发环境中,Simulink和Arduino的结合使用为创建智能机器人提供了强大的工具集。本次Webinar(网络研讨会)的主题是“matlab开发-使用...在Webinar提供的资料中,你将有机会亲身体验这种集成开发的强大之处。
【实施敏捷和变革困难】在于人们通常需要通过亲身体验来驱动改变,而非仅靠理论分析。抗拒改变常被视为阻碍,但实际上,它可能源于我们未能触动人们的内心感受。推动敏捷和变革的关键在于创造条件,让人们能够直接...
7. 使用Cucumber进行行为驱动开发(BDD):Cucumber是一种行为驱动开发工具,它允许通过自然语言(如Gherkin语言)编写功能测试用例。本书将向读者介绍如何将Cucumber集成到Spring Boot项目中,用以实现BDD。 整...
TDD(测试驱动开发)和BDD(行为驱动开发)是常用的测试策略,自动化测试工具如JUnit和Selenium也有广泛应用。 5. 项目管理:使用敏捷方法如Scrum或Kanban管理项目,进行迭代开发,确保项目进度和质量。项目管理...
《VB三维游戏RevoTron完整源代码》是一款基于Visual Basic(VB)开发的三维游戏,它展示了VB在游戏开发中...通过实际操作和研究,你可以亲身体验到从零开始构建一款游戏的全过程,这无疑将大大加深你对游戏开发的理解。
6. **Spock测试框架**:Groovy社区还开发了Spock,一个强大的行为驱动开发(BDD)测试框架,源码中可能会包含使用Spock进行单元测试和集成测试的例子。 7. **DSL(Domain Specific Language)**:Groovy的语法灵活...
《Playmaker:Unity中的可视化脚本利器》 在游戏开发领域,Unity引擎因其强大的功能和易用...通过下载并导入"Playmaker 1.8.5.unitypackage",你将能够亲身体验到这个工具的强大之处,并可能开启一个全新的开发体验。
通过亲身参与游戏的设计与开发,学习者能够将C++的语法知识、面向对象编程理论、事件驱动模式以及逻辑设计技能,转化为解决实际问题的能力。这种项目驱动的学习方式,极大地提高了学习的积极性和实践性,有助于培养...
5. **系统调用设计与调试**:实验五让学生亲身体验添加新的系统调用,包括系统调用接口、参数传递、返回值处理等。这一环节将深入到内核与用户空间的交互机制。 6. **同步机制**:实验六聚焦于内核同步,包括信号量...
体验式学习理论的引入,强调了让学员通过亲身体验来学习的重要性。这种理论特别适用于成人学习,因为成人学习者倾向于在实际应用中学习新知识,而非单纯从理论上理解。因此,体验式学习强调让学员参与到各种活动中,...
通过实际编写俄罗斯方块游戏,学习者可以亲身体验到这些面向对象概念如何应用于实际问题解决。例如,设计一个游戏循环,处理用户的输入,更新方块的位置,检查消除行的情况,以及生成新的方块等。这些过程都需要灵活...
这部分将引导你实际编写和运行一个简单的MFC程序,让你亲身体验MFC编程的过程。通过实际操作,你可以更好地理解AppWizard生成的MFC代码结构,并学习如何进行代码扩展和修改。 3. **第三部分:MFC样式** 这一部分...