今天我要完成这个游戏的核心功能(猜测数字),我称它为Guesser。传入一个数字,返回一个结果。分析一下它可能输出的几种情况:4a0b(全对)、 0a4b(数字全对,位置全错)、2a2b(一半一半)、0a0b(全错)。这4个CASE应该Cover了所有情况了。如果有补充,请Follow。
今天的TO-DO-LIST:
假设我们这局游戏的答案是2975。
输入“2975” 输出4a0b。
输入“2957” 输出2a2b。
输入“9257” 输出0a4b。
输入“1348” 输出0a0b。
完成Guesser类.[TestMethod] public void Test1() { var inputNumber = "2975"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("4a0b", actual); }
[TestMethod] public void should_return_4a0b_when_input_numbers_all_figures_and_positions_are_right() { var inputNumber = "2975"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("4a0b", actual); }
修改完之后在看这个方法的命名。 是不是清晰了很多。看到方法的命名应该就能够猜测到此方法是在什么情况下测试什么功能。
OK。我们的第一个CASE搞定了。Run一下。
编译不通过!因为没有Guesser类。没有Guess方法。
为了让CASE过。我们必须创建Guesser类以及Guess方法。
public class Guesser { public string Guess(string inputNumber) { throw new System.NotImplementedException(); } }
现在编译没错误了。在Run。
有异常:System.NotImplementedException: The method or operation is not implemented.
闹心。为了不闹心。Guess方法里简单实现。最简单的就是直接返回"4a0b"。这里还是简单的实现实现。
public class Guesser { private const string AnswerNumber = "2975"; public string Guess(string inputNumber) { var ACount = 0; var BCount = 0; for (var index = 0; index < AnswerNumber.Length; index++) { if (AnswerNumber[index]==inputNumber[index]) { ACount++; } } return string.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] public void should_return_2a2b_when_input_numbers_all_figures_right_and_2_positions_right() { var inputNumber = "2957"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("2a2b", actual); }
Run.....
没有PASS:Assert.AreEqual failed. Expected:<2a2b>. Actual:<2a0b>.
我们接着去改guess方法,保证第二个CASE PASS。
public class Guesser { private const string AnswerNumber = "2975"; public string Guess(string inputNumber) { var aCount = 0; var bCount = 0; for (var index = 0; index < AnswerNumber.Length; index++) { if (AnswerNumber[index]==inputNumber[index]) { aCount++; continue; } if (AnswerNumber.Contains(inputNumber[index].ToString())) { bCount++; } } return string.Format("{0}a{1}b", aCount, bCount); } }运行所有CASE...pass.别忘记划掉To-Do-List
[TestMethod] public void should_return_4a0b_when_input_numbers_all_figures_and_positions_are_right() { var inputNumber = "2975"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("4a0b", actual); } [TestMethod] public void should_return_2a2b_when_input_numbers_all_figures_right_and_2_positions_right() { var inputNumber = "2957"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("2a2b", actual); } [TestMethod] public void should_return_0a4b_when_input_numbers_all_figures_right_and_no_positions_right() { var inputNumber = "9257"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("0a4b", actual); } [TestMethod] public void should_return_0a0b_when_input_number_all_figures_and_positions_wrong() { var inputNumber = "1348"; var actual = new Guesser().Guess(inputNumber); Assert.AreEqual("0a0b", actual); }运行所有CASE...
不知不觉我们已经完成了今天的所有任务。
To-Do-List:
输入“2975” 输出4a0b。
输入“2957” 输出2a2b。
输入“9257” 输出0a4b。
输入“1348” 输出0a0b。
完成Guesser类.
Go Home.然后别忘记把总的List划掉。
今天的任务完成。今天收获是什么?
(1)Test First。从用户角度去思考问题。在设计CASE之前。会把关注点放到需求上。只有足够透彻的了解需求。才能设计出正确全面的CASE。
(2)命名。在最开始我们把Test1改成了‘should_return_4a0b_when_input_numbers_all_figures_and_positions_are_right’。看到方法名,就相当于看到文档。很快速的知道当前方法测的是什么功能。而不需要去翻阅文档。维护起来也是相当清晰。不用花大把时间去维护文档。
当然也有些疑问。
(1)先写测试在写代码开发速度降低了。
带着这些疑问。继续做下去。希望在这个系统实现完之后能解决我的疑问。
最后Yuheng同学提出了一个问题。
有4个分别是[风险高 价值高]、[风险高 价值低]、[风险低 价值高]、[风险低 价值低]的事情,你会优先做哪个?
大家可以给出自己的答案。
如果哪位对TDD有兴趣或者想讨论的可Follow或者http://my.csdn.net/wxr0323
(完)