测试驱动开发TDD(3)

简介:
 上一篇我剩下的To-Do-List:
  猜测数字
  输入验证
  生成答案
  输入次数
  输出猜测结果
  今天争取全部搞定。
  现在我们Guesser、生成答案、输入验证都有了。把它们组装成一起摇身一变成一个Game!
  用一个类把这些职责单一的小模块组合起来。我暂且称它为GameManager.
  分析剩下的需求。(1)输入6次GameOver.(2)输入合法数字返回猜测结果。(3)游戏结束提示重新开始游戏。(4)中途输入exit 退出游戏。(5)输入正确答案,GameOver。
  先把之前写的Guesser提出一个接口。
public interface IGuesser
{
string AnswerNumber { get; }
string Guess(string inputNumber);
}
public class Guesser :IGuesser
{
public string AnswerNumber { get; private set; }
public Guesser(IAnswerGenerator generator)
{
AnswerNumber = generator.Generate();
}
public string Guess(string inputNumber)
{
...
}
}
    Test First.
  新建GameManagerTest
[TestClass]
public class GameManagerTest
{
[TestMethod]
public void should_return_game_over_when_input_times_is_six_and_result_is_wrong()
{
IGuesser guess = new Guesser(new AnswerGeneratorForTest());
var game = new GameManager(guess);
var input = "1368";
var maxtimes = 6;
var actual = false;
for (var time = 0; time < maxtimes; time++)
{
game.Guess(input);
}
actual = game.IsGameOver;
Assert.AreEqual(true, actual);
}
}
 实现GameManager让测试通过。
public class GameManager
{
private readonly IGuesser guesser;
public bool IsGameOver { get; private set; }
private const int MAX_TIMES = 6;
private int times;
public GameManager(IGuesser guesser)
{
this.guesser = guesser;
}
public void Guess(string input)
{
times++;
IsGameOver = times == MAX_TIMES;
guesser.Guess(input);
}
}
    ok
  猜测数字
  输入验证
  生成答案
  输入次数
  输出猜测结果
  输入猜测结果。这里包含一个猜对的情况下应该返回"you win"并且Gameover。其他输入返回的结果,在Guesser和validator中已经Cover了。挑几个来测试一下输入输出。
[TestClass]
public class GameManagerTest
{
private _gameManager _game;
[TestInitialize]
public void Init()
{
IGuesser guesser = new Guesser(new AnswerGeneratorForTest());
_game = new _gameManager(guesser);
}
[TestMethod]
public void should_return__game_over_when_input_times_is_six_and_result_is_wrong()
{
const string input = "1368";
const int maxtimes = 6;
var actual = false;
for (var time = 0; time < maxtimes; time++)
{
_game.Guess(input);
}
actual = _game.Is_gameOver;
Assert.AreEqual(true, actual);
}
[TestMethod]
public void should_return_you_win_and__game_is_over_when_input_is_equal_answer_number()
{
const string input = "2975";
var actual = _game.Guess(input);
Assert.AreEqual("You win!", actual);
Assert.AreEqual(true, _game.Is_gameOver);
}
[TestMethod]
public void should_return_try_again_input_must_be_four_digits_when_input_is_not_equal_four_digits()
{
const string input = "15243";
var actual = _game.Guess(input);
Assert.AreEqual("try again the input must be four digits.", actual);
}
[TestMethod]
public void should_return_try_again_input_can_not_be_empty_when_input_is_empty()
{
const string input = "";
var actual = _game.Guess(input);
Assert.AreEqual("try again the input can't be empty.", actual);
}
[TestMethod]
public void should_return_try_again_input_must_be_fully_digital_when_input_is_not_all_digital()
{
const string input = "a4sw";
var actual = _game.Guess(input);
Assert.AreEqual("try again the input must be fully digital.", actual);
}
}


修改GameManager类,让所有CASE PASS.
public string Guess(string input)
{
times++;
IsGameOver = times == MAX_TIMES;
var validator = new Validator();
if (!validator.Validate(input))
{
return "try again " + validator.ErrorMsg;
}
var result = guesser.Guess(input);
if (result == "4a0b")
{
IsGameOver = true;
return "You win!";
}
return "try again result is " + result + ".";
}
  猜测数字
  输入验证
  生成答案
  输入次数
  输出猜测结果
  最后:完善GameManager类的work flow。
public class GameManager
{
private const int MAX_TIMES = 6;
private int times;
private readonly IGuesser guesser;
public bool IsGameOver { get; private set; }
public GameManager(IGuesser guesser)
{
this.guesser = guesser;
}
private void Start()
{
times = 0;
IsGameOver = false;
OutputGameHeader();
}
public void Run()
{
Start();
while (!IsGameOver)
{
Console.WriteLine();
Console.WriteLine(string.Format("[The {0}st time ] : please input number!", times + 1));
var input = Console.ReadLine();
if (IsExit(input)) continue;
var result = Guess(input);
Console.WriteLine(result);
}
OutputGamefooter();
}
private bool IsExit(string input)
{
if (input.ToLower().Trim() == "exit")
{
Console.WriteLine("Make sure to exit game?(Y/N)");
var readLine = Console.ReadLine();
if (readLine != null)
{
var isexit = readLine.ToLower().Trim();
if (isexit == "y")
{
IsGameOver = true;
}
}
return true;
}
return false;
}
public string Guess(string input)
{
times++;
IsGameOver = times == MAX_TIMES;
var validator = new Validator();
if (!validator.Validate(input))
{
return "try again " + validator.ErrorMsg;
}
var result = guesser.Guess(input);
if (result == "4a0b")
{
IsGameOver = true;
return "You win!";
}
return "try again result is " + result + ".";
}
private void OutputGameHeader()
{
Console.Clear();
Console.WriteLine("                  --- Game Start! ---");
Console.WriteLine("---------------------------------------------------------------");
Console.WriteLine("| You can input a number or input exit for exiting this game! |");
Console.WriteLine("---------------------------------------------------------------");
}
private void OutputGamefooter()
{
Console.WriteLine("--------------------------------");
Console.WriteLine("| Game Over!  [Answer] is " + guesser.AnswerNumber + " |");
Console.WriteLine("--------------------------------");
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
var isRepeatGame = false;
do
{
IGuesser guesser = new Guesser(new AnswerGenerator());
var game = new GameManager(guesser);
game.Run();
Console.WriteLine("Try again?(Y/N)");
var line = Console.ReadLine();
if (line == null) continue;
var readLine = line.ToLower().Trim();
isRepeatGame = readLine == "y";
} while (isRepeatGame);
}
}
  跑下所有的测试。
到这里。这个游戏的基本功能算做完了。做的比较简单。测试和产品代码也比较随意。
  大家也可以试着做一做。感受感受测试驱动产品代码。
  运行效果
  下面是我在实践TDD中遇到的一些问题、以及我个人对它们的理解。
  (1)先写测试在写代码开发速度降低了。
  开发前期速度确实很慢。当项目越来越大。越来越复杂的时候。改一个bug,或者修改story之后。如何确保产品代码是否正确。手动测试需要多少时间呢?或者调试的时间有多长呢?有了这些测试。可以最大限度的节省你的时间。也许跑一遍测试就可以定位BUG。测试过了。你的修改也就没问题了。
  (2)TDD驱动出来的代码。维护性、扩展性如何。
  TDD有益于设计。把一个复杂的需求拆分成若干个小模块的过程当中,其实就在思考设计。如何保证每个小模块的职责单一。
  (3)后写测试可以不?
  我的理解是:第一 测试驱动开发是通过测试去驱动产品代码的,如果遇到一个很难的模块(你写不出来的),就可以通过测试一点点的去驱动。第二 如果在开发之后写测试的话,问问自己,会写吗?或者是能写全吗?如果有足够的信心。也可以写。
  (4)TDD的产物可以方便后期的测试。
  试想一下,项目到了后期,在庞大的系统面前,我们要修改某个类、某个方法、修改某个BUG、添加或扩展某个功能的时候。是不感觉特没安全感?会不会为了去找由修改一个小功能而导致其他功能崩溃的原因而抓狂呢?会不会为了定位一个BUG而在各个类之间不断的徘徊呢?会不会感觉到牵一发动全身的感觉呢?软件的坏味等等都会导致这种问题出现。到时候不但被老板骂,还要加班!还要陷入无止尽的各种调试中。(最主要的是你把TEAM里的MM给连累了!)。想避免这种问题吗?想尽快定位BUG的位置吗?如果你想!
   说点体会:
  (1)清晰的测试方法命名,让我们省去了文档维护的时间。
  (2)站在不同角度分析用户需求。拆分Story有益于你的设计(DI)。
  (3)所有TDD留下来的测试。可用来做自动化测试,无论你是修BUG,或者添功能。都可以通过自动化测试,快速得到反馈。
  (4)有了重构的保证。
  (5)进度可视化。可以看出一个复杂的模块,自己完成了多少。(有多少CASE通过)。
  (6)小范围迭代。把当前工作重心放在当前这个“小步”上。
   需要注意的:
  (1)把握好测试的粒度。
  (2)测试要尽可能的简单。
  (3)测试不要依赖可变。
  (4)断言优先。
  其实TDD真正有威力的地方是Story划分。以及复杂模块代码的驱动。
  以后如果有机会。能理解的更深。会把这两个写出来与大家分享分享。
  对这段时间的TDD做个小总结。TDD的范围比较广。而且也比较抽象。以后会加深对TDD的理解。也会把这些记录下来。
  代码比较简单。没源码!

最新内容请见作者的GitHub页:http://qaseven.github.io/

相关文章
|
6月前
|
Devops Java 测试技术
软件测试/测试开发|常见软件测试框架类型:TDD、BDD、DDD、ATDD、DevOps介绍
软件测试/测试开发|常见软件测试框架类型:TDD、BDD、DDD、ATDD、DevOps介绍
127 0
|
6月前
|
监控 前端开发 JavaScript
如何测试前端代码? 知道 BDD, TDD, Unit Test 么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)?
如何测试前端代码? 知道 BDD, TDD, Unit Test 么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)?
134 0
|
3月前
|
Java 测试技术 开发者
提高代码质量:深入实践测试驱动开发(TDD)
【8月更文挑战第14天】测试驱动开发是一种强大的软件开发方法,它通过先写测试再编写代码的方式,显著提高了代码质量。通过实践TDD,开发者可以编写出更可靠、更易于维护的代码,并加速开发进程。虽然TDD需要一定的学习和适应过程,但其带来的长期收益是不可估量的。如果你还没有尝试过TDD,现在就开始吧!
|
3月前
|
测试技术 Go
写出高质量代码的秘诀:Golang中的测试驱动开发(TDD)
写出高质量代码的秘诀:Golang中的测试驱动开发(TDD)
|
4月前
|
Java 测试技术
Java中的测试驱动开发(TDD)实践
Java中的测试驱动开发(TDD)实践
|
6月前
|
敏捷开发 测试技术
深入理解并应用测试驱动开发(TDD)
【5月更文挑战第30天】测试驱动开发(TDD)是一种先编写测试用例再写代码的方法论,核心是&quot;先测试,后开发&quot;。通过&quot;红-绿-重构&quot;的循环,确保代码正确性,提升简洁性和可维护性。TDD步骤包括编写测试用例、运行测试、编写实现代码、重构及循环迭代。它能提高代码质量,促进团队合作,降低维护成本,并适应敏捷开发。在开发实践中,应用TDD能有效提升效率和代码质量。
|
6月前
|
算法 测试技术 开发者
测试驱动开发(TDD)实战:从理论到实践
【5月更文挑战第8天】TDD实战指南:先测试后开发,确保代码质量与可维护性。核心思想是编写测试用例→实现代码→验证→重构。优点包括提高代码质量、促进设计思考和增强可测试性。实战步骤包括编写独立、明确的测试用例,遵循最小可用原则编写代码,运行测试并分析失败原因,以及在验证通过后进行代码重构与优化。通过TDD,开发者能提升编程技能和项目成功率。
|
6月前
|
监控 前端开发 JavaScript
如何测试前端代码? 知道 BDD, TDD, Unit Test 么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)?
如何测试前端代码? 知道 BDD, TDD, Unit Test 么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)?
70 0
|
6月前
|
测试技术 持续交付 Swift
【Swift开发专栏】Swift中的测试驱动开发(TDD)
【4月更文挑战第30天】Test-Driven Development (TDD) 是一种软件开发方法,强调先编写测试用例再写代码。通过测试驱动代码、简明设计、重构和持续集成的循环过程,TDD助力构建高质量Swift软件。在Swift中,使用XCTest框架进行TDD实践,包括编写测试用例、实现功能、运行测试和重构。TDD的优势在于提升代码质量、减少调试时间,且与持续集成相结合。然而,学习曲线和确保测试覆盖率是挑战。TDD不仅是技术实践,也是思维方式,随着Swift的发展,其应用将更广泛。
58 3
|
6月前
|
测试技术 开发者
【专栏】测试驱动开发(TDD)和行为驱动开发(BDD)的核心概念与实践
【4月更文挑战第27天】本文探讨了测试驱动开发(TDD)和行为驱动开发(BDD)的核心概念与实践。TDD强调先写测试用例,通过测试推动设计,确保代码质量与可维护性。BDD侧重软件行为和业务价值,提倡使用通用语言描述行为,减少沟通障碍。选择TDD或BDD取决于项目复杂性、团队技能和业务需求。理解两者差异有助于团队做出合适的选择,发挥测试的最大价值。
146 3