今儿接到一需求如下:
比如一个给定的数字2975,让你去猜。6次机会。如果第一次输入2509,系统会提示 1A2B:其中数字“2”位置猜对&&数字也猜对。称为1A,而“9”和“5”数字猜对了但是位置没有猜对。称为2B。。如果输入2975那么就是4个数字都猜对了并且位置也是对的系统提示4A0B。民间俗称猜数字游戏:百度百科传送门:http://baike.baidu.com/view/358630.htm。
做个简单分析。客户端输入一个数字,经过游戏内部的猜测,返回一个结果给客户端。嗯,还好,不算难。由于准备做TDD实践。所以我们Test First.Why? 如果您对TDD不是很了解。就跟我一起做下去,显然我也是新手。我们的目标是“没有蛀牙!”。
准备工作:VS任意一个版本(C#)、任意一款测试工具、纸、笔(真彩0.5的)。
第一步:笔和纸拿出来。思考思考如何把这个小游戏拆分了。然后我们一步一步去完成它。写一个To-Do-List。
To-Do-List:
猜测数字
输入验证
生成答案
输入次数
输出猜测结果
...............
暂时想起这么多。比如还有选择游戏难度、输入日志、重新开始游戏、中途退出等等。
今天我要完成这个游戏的核心功能(猜测数字),我称它为Guesser。传入一个数字,返回一个结果。分析一下它可能输出的几种情况:4a0b(全对)、 0a4b(数字全对,位置全错)、2a2b(一半一半)、0a0b(全错)。这4个CASE应该Cover了所有情况了。如果有补充,请Follow。
今天的TO-DO-LIST:
假设我们这局游戏的答案是2975。
输入“2975” 输出4a0b。
输入“2957” 输出2a2b。
输入“9257” 输出0a4b。
输入“1348” 输出0a0b。
完成Guesser类.新建一个TEST 写测试。
开始第一个CASE:输入2975 与答案正匹配,输出4a0b 。
- [TestMethod]
- publicvoidTest1()
- {
- varinputNumber="2975";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("4a0b",actual);
- }
这个测试方法的命名一眼好像看不出它要测什么。单独看测试的名字很是迷茫。我们修改一下让它看起来很整洁。一眼看上去就知道是啥意思。要测试什么。
- [TestMethod]
- publicvoidshould_return_4a0b_when_input_numbers_all_figures_and_positions_are_right()
- {
- varinputNumber="2975";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("4a0b",actual);
- }
修改完之后在看这个方法的命名。 是不是清晰了很多。看到方法的命名应该就能够猜测到此方法是在什么情况下测试什么功能。
OK。我们的第一个CASE搞定了。Run一下。
编译不通过!因为没有Guesser类。没有Guess方法。
为了让CASE过。我们必须创建Guesser类以及Guess方法。
- publicclassGuesser
- {
- publicstringGuess(stringinputNumber)
- {
- thrownewSystem.NotImplementedException();
- }
- }
现在编译没错误了。在Run。
有异常:System.NotImplementedException: The method or operation is not implemented.
闹心。为了不闹心。Guess方法里简单实现。最简单的就是直接返回"4a0b"。这里还是简单的实现实现。
- publicclassGuesser
- {
- privateconststringAnswerNumber="2975";
- publicstringGuess(stringinputNumber)
- {
- varACount=0;
- varBCount=0;
- for(varindex=0;index<AnswerNumber.Length;index++)
- {
- if(AnswerNumber[index]==inputNumber[index])
- {
- ACount++;
- }
- }
- returnstring.Format("{0}a{1}b",ACount,BCount);
- }
- }
OK。但是我们内部实现如此简单。不知道是否满足第二个CASE呢。
完成一个CASE要把它划掉。
To-Do-List:
输入“2975” 输出4a0b。
输入“2957” 输出2a2b。
输入“9257” 输出0a4b。
输入“1348” 输出0a0b。
完成Guesser类.
来第二个CASE:输入9257与答案不匹配,但是所有数字都正确,输出0a4b 。
有了第一个CASE的经验,这里我们也同样注意命名。
- [TestMethod]
- publicvoidshould_return_2a2b_when_input_numbers_all_figures_right_and_2_positions_right()
- {
- varinputNumber="2957";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("2a2b",actual);
- }
Run.....
没有PASS:Assert.AreEqual failed. Expected:<2a2b>. Actual:<2a0b>.
我们接着去改guess方法,保证第二个CASEPASS。
- publicclassGuesser
- {
- privateconststringAnswerNumber="2975";
- publicstringGuess(stringinputNumber)
- {
- varaCount=0;
- varbCount=0;
- for(varindex=0;index<AnswerNumber.Length;index++)
- {
- if(AnswerNumber[index]==inputNumber[index])
- {
- aCount++;
- continue;
- }
- if(AnswerNumber.Contains(inputNumber[index].ToString()))
- {
- bCount++;
- }
- }
- returnstring.Format("{0}a{1}b",aCount,bCount);
- }
- }
运行所有CASE...pass.别忘记划掉To-Do-List接下来把剩下的两个CASE搞定。
- [TestMethod]
- publicvoidshould_return_4a0b_when_input_numbers_all_figures_and_positions_are_right()
- {
- varinputNumber="2975";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("4a0b",actual);
- }
- [TestMethod]
- publicvoidshould_return_2a2b_when_input_numbers_all_figures_right_and_2_positions_right()
- {
- varinputNumber="2957";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("2a2b",actual);
- }
- [TestMethod]
- publicvoidshould_return_0a4b_when_input_numbers_all_figures_right_and_no_positions_right()
- {
- varinputNumber="9257";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("0a4b",actual);
- }
- [TestMethod]
- publicvoidshould_return_0a0b_when_input_number_all_figures_and_positions_wrong()
- {
- varinputNumber="1348";
- varactual=newGuesser().Guess(inputNumber);
- Assert.AreEqual("0a0b",actual);
- }
运行所有CASE...
不知不觉我们已经完成了今天的所有任务。
To-Do-List:
输入“2975” 输出4a0b。
输入“2957” 输出2a2b。
输入“9257” 输出0a4b。
输入“1348” 输出0a0b。
完成Guesser类.
GoHome.然后别忘记把总的List划掉。
今天的任务完成。今天收获是什么?
(1)TestFirst。从用户角度去思考问题。在设计CASE之前。会把关注点放到需求上。只有足够透彻的了解需求。才能设计出正确全面的CASE。
(2)命名。在最开始我们把Test1改成了‘should_return_4a0b_when_input_numbers_all_figures_and_positions_are_right’。看到方法名,就相当于看到文档。很快速的知道当前方法测的是什么功能。而不需要去翻阅文档。维护起来也是相当清晰。不用花大把时间去维护文档。
当然也有些疑问。
(1)先写测试在写代码开发速度降低了。
带着这些疑问。继续做下去。希望在这个系统实现完之后能解决我的疑问。
最后Yuheng同学提出了一个问题。
有4个分别是[风险高 价值高]、[风险高 价值低]、[风险低 价值高]、[风险低 价值低]的事情,你会优先做哪个?
大家可以给出自己的答案。
分享到:
相关推荐
code kata以及测试驱动开发TDD介绍实用PPT课件.pptx
测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术、Tips 等方面。 背景 一个...
个人总结TDD测试驱动开发TDD(1-3)
测试驱动开发 TDD ,将读者带入XP极限编程的神奇世界!
主要介绍的测试驱动的设计和开发,以及测试的工作模式。
TDD 测试驱动开发 测试驱动开发的艺术 Lasse Koskela 带目录结构
code kata以及测试驱动开发TDD介绍实用PPT学习教案.pptx
TDD测试驱动开发.pptx
教程主题:Visual Studio 2010 TDD 测试驱动开发 实战 教程录制:柳永法 web:http://www.yongfa365.com/ 本教程旨在以最简单的hello world方式像您展示 Visual Studio 2010强大的TDD,推荐任何没接触过VS2010及TDD...
测试驱动开发,软件开发人员必备啊,移动工具开发
java TDD测试开发流程 包含mekito测试的整套教程。 敏捷开发 适用
极限编程反其道而行之,主张采用测试驱动开发(TDD)的方法,即通过测试定义所要开发的功能的接口,然后实现功能的开发过程。TDD通过不断地测试推动代码的开发,既简化了代码,又保证了软件质量。本书采用“手把手”...
一般来讲程序员都愿意把功能完美的体现在代码上,可有时候天不随人意,心里免不得担忧,我这代码能满足...由于要先开发测试用例,那么开发人员就必须清楚测试的目的,这样TDD确保了项目的代码与所需的业务是匹配的。
要使测试驱动开发在软件行业中得以繁荣兴盛,需要一些条件,《C#测试驱动开发》从讨论这些条件开始。软件开发发展到今天,有其历史和特定的条件,理解这些很重要。避免重复过去的错误也很重要。在自己当前的开发实践...
TDD的概念与模式第二部分 针对特定技术应用TDD第5章 测试驱动Web组件第6章 测试驱动数据访问第7章 测试驱动不可预测功能第8章 测试驱动Swing代码第三部分 基于ATDD构建产品第9章 解析验收测试驱动开发第10章 ...
C#测试驱动开发(中文清晰版) 第Ⅰ部分 入门 第 1 章 通向测试驱动开发之路 第 2 章 单元测试简介 第 3 章 重构速览 第 4 章 测试驱动开发:以测试为指南 第 5 章 模拟外部资源 第Ⅱ部分 将基础知识...
《Java测试驱动开发》介绍如何将各种TDDzui佳实践应用于Java开发,主要内容包括:用Java语言进行TDD会用到的各种工具和框架,所需环境搭建;通过实际应用程序,展示TDD优点及开发中应注意的主要问题;TDD是如何通过...
《测试驱动开发的3项修炼:走出TDD丛林》用实际案例及故事讲述了测试驱动开发(TDD)的最佳实践,从TDD为什么实践起来非常困难等最根源的问题入手,循序渐进地介绍了构筑TDD的三项修炼,涉及到未雨绸缪的单元及自动化...