`
yanfaguanli
  • 浏览: 658551 次
文章分类
社区版块
存档分类
最新评论

测试驱动开发TDD(二)

 
阅读更多

大家好:

今天的TDD练习又开始了。回头看看上一次留下的任务。

To-Do-List:

猜测数字
输入验证
生成答案
输入次数
输出猜测结果

今天我们把输入验证和随机生成答案搞定。

新建ValidationTest文件。

分析需求:(1)不重复。(2)4位(3)数字。(4)不为空。

按照我们分析出来的4个明确点我们开始写CASE。

注意命名!

[csharp] view plaincopy
  1. [TestClass]
  2. publicclassValidatorTest
  3. {
  4. privateValidatorvalidator;
  5. [TestInitialize]
  6. publicvoidInit()
  7. {
  8. validator=newValidator();
  9. }
  10. [TestMethod]
  11. publicvoidshould_return_input_must_be_four_digits_when_input_figures_digit_is_not_four_digits()
  12. {
  13. varinput="29546";
  14. validator.Validate(input);
  15. varactual=validator.ErrorMsg;
  16. Assert.AreEqual("theinputmustbefourdigits.",actual);
  17. }
  18. [TestMethod]
  19. publicvoidshould_return_input_must_be_fully_digital_when_input_is_not_all_digital()
  20. {
  21. varinput="a4s5";
  22. validator.Validate(input);
  23. varactual=validator.ErrorMsg;
  24. Assert.AreEqual("theinputmustbefullydigital.",actual);
  25. }
  26. [TestMethod]
  27. publicvoidshould_return_input_can_not_be_empty_when_input_is_empty()
  28. {
  29. varinput="";
  30. validator.Validate(input);
  31. varactual=validator.ErrorMsg;
  32. Assert.AreEqual("theinputcan'tbeempty.",actual);
  33. }
  34. [TestMethod]
  35. publicvoidshould_return_input_can_not_contain_duplicate_when_input_figures_contain_duplicate()
  36. {
  37. varinput="2259";
  38. validator.Validate(input);
  39. varactual=validator.ErrorMsg;
  40. Assert.AreEqual("theinputfigurescan'tcontainduplicate.",actual);
  41. }
  42. }
按照第一篇的步骤。实现validator。争取让所有的CASE都过。

[csharp] view plaincopy
  1. publicclassValidator
  2. {
  3. publicstringErrorMsg{get;privateset;}
  4. publicboolValidate(stringinput)
  5. {
  6. if(string.IsNullOrEmpty(input))
  7. {
  8. ErrorMsg="theinputcan'tbeempty.";
  9. returnfalse;
  10. }
  11. if(input.Length!=4)
  12. {
  13. ErrorMsg="theinputmustbefourdigits.";
  14. returnfalse;
  15. }
  16. varregex=newRegex(@"^[0-9]*$");
  17. if(!regex.IsMatch(input))
  18. {
  19. ErrorMsg="theinputmustbefullydigital.";
  20. returnfalse;
  21. }
  22. if(input.Distinct().Count()!=4)
  23. {
  24. ErrorMsg="theinputfigurescan'tcontainduplicate.";
  25. returnfalse;
  26. }
  27. returntrue;
  28. }
  29. }

Run...


一个CASE对应这一个IF。也可合并2个CASE。可以用"^\d{4}$"去Cover"4位数字"。可以根据自己的情况去定。

小步前进不一定要用很小粒度去一步一步走。这样开发起来的速度可能很慢。依靠你自身的情况去决定这一小步到底应该有多大。正所谓"步子大了容易扯到蛋,步子小了前进太慢"。只要找到最合适自己的步子。才会走的更好。

这么多IF看起来很蛋疼。有测试。可以放心大胆的重构。把每个IF抽出一个方法。看起来要清晰一些。

[csharp] view plaincopy
  1. publicclassValidator
  2. {
  3. publicstringErrorMsg{get;privateset;}
  4. publicboolValidate(stringinput)
  5. {
  6. returnIsEmpty(input)&&IsFourdigits(input)&&IsDigital(input)&&IsRepeat(input);
  7. }
  8. privateboolIsEmpty(stringinput)
  9. {
  10. if(!string.IsNullOrEmpty(input))
  11. {
  12. returntrue;
  13. }
  14. ErrorMsg="theinputcan'tbeempty.";
  15. returnfalse;
  16. }
  17. privateboolIsFourdigits(stringinput)
  18. {
  19. if(input.Length==4)
  20. {
  21. returntrue;
  22. }
  23. ErrorMsg="theinputmustbefourdigits.";
  24. returnfalse;
  25. }
  26. privateboolIsDigital(stringinput)
  27. {
  28. varregex=newRegex(@"^[0-9]*$");
  29. if(regex.IsMatch(input))
  30. {
  31. returntrue;
  32. }
  33. ErrorMsg="theinputmustbefullydigital.";
  34. returnfalse;
  35. }
  36. privateboolIsRepeat(stringinput)
  37. {
  38. if(input.Distinct().Count()==4)
  39. {
  40. returntrue;
  41. }
  42. ErrorMsg="theinputfigurescan'tcontainduplicate.";
  43. returnfalse;
  44. }
  45. }

为了确保重构正确。重构之后一定要把所有的CASE在跑一遍,确定所有的都PASS。

To-Do-List:
猜测数字
输入验证
生成答案
输入次数
输出猜测结果

验证搞定了。我们来整整随机数。

分析需求:产品代码需要一个随机生成的答案。(1)不重复。(2)4位(3)数字。

这里有个问题:大家都知道随机数是个概率的问题。因为每次生成的数字都不一样。看看之前Guesser类的代码。

[csharp] view plaincopy
  1. publicclassGuesser
  2. {
  3. privateconststringAnswerNumber="2975";
  4. publicstringGuess(stringinputNumber)
  5. {
  6. varaCount=0;
  7. varbCount=0;
  8. for(varindex=0;index<AnswerNumber.Length;index++)
  9. {
  10. if(AnswerNumber[index]==inputNumber[index])
  11. {
  12. aCount++;
  13. continue;
  14. }
  15. if(AnswerNumber.Contains(inputNumber[index].ToString()))
  16. {
  17. bCount++;
  18. }
  19. }
  20. returnstring.Format("{0}a{1}b",aCount,bCount);
  21. }
  22. }

这里我们如果把private const string AnswerNumber = "2975";改为随机的话,那Guesser类测试的结果是不能确定的。也就是说测试依赖了一些可变的东西。比如:随机数、时间等等。

遇到这种情况应该怎么办呢?一种随机数是给产品代码用,我们可以MOCK另外一种"固定随机数"(但是要满足生成随机数的条件)来给测试用。

还是一样先写测试。

[csharp] view plaincopy
  1. [TestClass]
  2. publicclassAnswerGeneratorTest
  3. {
  4. [TestMethod]
  5. publicvoidshould_pass_when_answer_generator_number_is_four_digits_and_fully_digital()
  6. {
  7. Regexregex=newRegex(@"^\d{4}$");
  8. varanswerGenerator=newAnswerGenerator();
  9. varactual=regex.IsMatch(answerGenerator.Generate());
  10. Assert.AreEqual(true,actual);
  11. }
  12. [TestMethod]
  13. publicvoidshould_pass_when_answer_generator_number_do_not_repeat()
  14. {
  15. varanswerGenerator=newAnswerGenerator();
  16. varactual=answerGenerator.Generate().Distinct().Count()==4;
  17. Assert.AreEqual(true,actual);
  18. }
  19. }

实现AnswerGenerator类让测试通过。

引用cao大一段代码稍加修改

[csharp] view plaincopy
  1. publicclassAnswerGenerator
  2. {
  3. publicstringGenerate()
  4. {
  5. varanswerNumber=newStringBuilder();
  6. Enumerable.Range(0,9)
  7. .Select(x=>new{v=x,k=Guid.NewGuid().ToString()})
  8. .OrderBy(x=>x.k)
  9. .Select(x=>x.v)
  10. .Take(4).ToList()
  11. .ForEach(num=>answerNumber.Append(num.ToString()));
  12. returnanswerNumber.ToString();
  13. }
  14. }

运行测试。


为了解决测试依赖可变的问题。定义IAnswerGenerator。让两种随机数类继承。

[csharp] view plaincopy
  1. publicinterfaceIAnswerGenerator
  2. {
  3. stringGenerate();
  4. }

[csharp] view plaincopy
  1. publicclassAnswerGenerator:IAnswerGenerator
  2. {
  3. publicstringGenerate()
  4. {
  5. varanswerNumber=newStringBuilder();
  6. Enumerable.Range(0,9)
  7. .Select(x=>new{v=x,k=Guid.NewGuid().ToString()})
  8. .OrderBy(x=>x.k)
  9. .Select(x=>x.v)
  10. .Take(4).ToList()
  11. .ForEach(num=>answerNumber.Append(num.ToString()));
  12. returnanswerNumber.ToString();
  13. }
  14. }

[csharp] view plaincopy
  1. publicclassAnswerGeneratorForTest:IAnswerGenerator
  2. {
  3. publicstringGenerate()
  4. {
  5. return"2975";
  6. }
  7. }

AnswerGenerator给产品代码用。AnswerGeneratorForTest给测试代码用。这样就可以避免测试依赖可变的问题。

相应的给Guesser类以及测试代码做个修改。

[csharp] view plaincopy
  1. publicclassGuesser
  2. {
  3. publicstringAnswerNumber{get;privateset;}
  4. publicGuesser(IAnswerGeneratorgenerator)
  5. {
  6. AnswerNumber=generator.Generate();
  7. }
  8. publicstringGuess(stringinputNumber)
  9. {
  10. ...
  11. }
  12. }

[csharp] view plaincopy
  1. [TestClass]
  2. publicclassGuesserTest
  3. {
  4. privateGuesserguesser;
  5. [TestInitialize]
  6. publicvoidInit()
  7. {
  8. guesser=newGuesser(newAnswerGeneratorForTest());
  9. }
  10. ...
  11. }

这样我在测试代码当中就会给一个不可变的随机数。AnswerGeneratorForTest。所以之前的Guesser测试代码也不会因为每次的随机数不一样导致挂掉。

产品代码呢?直接丢AnswerGenerator进去就妥妥地。

To-Do-List:
猜测数字
输入验证
生成答案
输入次数
输出猜测结果

OK。今天的收获。

(1)小步前进:依靠自身情况决定“小步”应该有多大。

(2)重构:之前的测试是我们重构的保障。

(3)测试依赖:测试不应该依赖于一些可变的东西。

都到这了,有没有点TDD的感觉。知道TDD的步骤了吗?

(1)新增一个测试。

(2)运行所有的测试程序并失败。

(3)做一些小小的改动。

(4)运行所有的测试,并且全部通过。

(5)重构代码以消除重复设计,优化设计。

(6)重复上面的工作。实现1~5小范围迭代。直到满足今天的Story。

上一篇还有个遗留的问题。我把它记在小本上。

(完)

分享到:
评论

相关推荐

    code kata以及测试驱动开发TDD介绍实用PPT课件.pptx

    code kata以及测试驱动开发TDD介绍实用PPT课件.pptx

    测试驱动开发(TDD)入门讲解及代码实例

    测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术、Tips 等方面。 背景 一个...

    测试驱动开发TDD(1-3)

    个人总结TDD测试驱动开发TDD(1-3)

    测试驱动开发(TDD)深入浅出

    测试驱动开发 TDD ,将读者带入XP极限编程的神奇世界!

    TDD测试驱动开发.pptx

    TDD测试驱动开发.pptx

    测试驱动开发TDD培训讲义

    主要介绍的测试驱动的设计和开发,以及测试的工作模式。

    TDD测试驱动开发

    TDD 测试驱动开发 测试驱动开发的艺术 Lasse Koskela 带目录结构

    code kata以及测试驱动开发TDD介绍实用PPT学习教案.pptx

    code kata以及测试驱动开发TDD介绍实用PPT学习教案.pptx

    测试驱动开发(TDD)

    测试驱动开发,软件开发人员必备啊,移动工具开发

    Visual Studio 2010 TDD 测试驱动开发 实战 视频及源码

    教程主题:Visual Studio 2010 TDD 测试驱动开发 实战 教程录制:柳永法 web:http://www.yongfa365.com/ 本教程旨在以最简单的hello world方式像您展示 Visual Studio 2010强大的TDD,推荐任何没接触过VS2010及TDD...

    TDD 测试驱动开发 文档 详细

    java TDD测试开发流程 包含mekito测试的整套教程。 敏捷开发 适用

    测试驱动开发的艺术 epub电子书

    极限编程反其道而行之,主张采用测试驱动开发(TDD)的方法,即通过测试定义所要开发的功能的接口,然后实现功能的开发过程。TDD通过不断地测试推动代码的开发,既简化了代码,又保证了软件质量。本书采用“手把手”...

    TDD是测试驱动开发

    一般来讲程序员都愿意把功能完美的体现在代码上,可有时候天不随人意,心里免不得担忧,我这代码能满足...由于要先开发测试用例,那么开发人员就必须清楚测试的目的,这样TDD确保了项目的代码与所需的业务是匹配的。

    测试驱动开发的艺术

    TDD的概念与模式第二部分 针对特定技术应用TDD第5章 测试驱动Web组件第6章 测试驱动数据访问第7章 测试驱动不可预测功能第8章 测试驱动Swing代码第三部分 基于ATDD构建产品第9章 解析验收测试驱动开发第10章 ...

    C#测试驱动开发

    要使测试驱动开发在软件行业中得以繁荣兴盛,需要一些条件,《C#测试驱动开发》从讨论这些条件开始。软件开发发展到今天,有其历史和特定的条件,理解这些很重要。避免重复过去的错误也很重要。在自己当前的开发实践...

    java测试驱动开发教程+代码实例

    《Java测试驱动开发》介绍如何将各种TDDzui佳实践应用于Java开发,主要内容包括:用Java语言进行TDD会用到的各种工具和框架,所需环境搭建;通过实际应用程序,展示TDD优点及开发中应注意的主要问题;TDD是如何通过...

    测试驱动开发的3项修炼-走出TDD丛林

    《测试驱动开发的3项修炼:走出TDD丛林》用实际案例及故事讲述了测试驱动开发(TDD)的最佳实践,从TDD为什么实践起来非常困难等最根源的问题入手,循序渐进地介绍了构筑TDD的三项修炼,涉及到未雨绸缪的单元及自动化...

    C#测试驱动开发(中文清晰版)

    C#测试驱动开发(中文清晰版) 第Ⅰ部分 入门 第 1 章 通向测试驱动开发之路 第 2 章 单元测试简介 第 3 章 重构速览 第 4 章 测试驱动开发:以测试为指南 第 5 章 模拟外部资源 第Ⅱ部分 将基础知识...

Global site tag (gtag.js) - Google Analytics