对应的outputPrinter可以做如下的微调:

public class OutputPrinter {
    public void print(String result) {

    }
}
  后别忘了启动Expectation验证:

context.assertIsSatisfied();


  整个测试方法现在看起来应该是这样的:

@Test                                                                            
 public void should_play_game_and_win() {                                         
     Mockery context = new JUnit4Mockery() {                                      
         {                                                                        
             setImposteriser(ClassImposteriser.INSTANCE);                         
         }                                                                        
     };                                                                           
     final AnswerGenerator answerGenerator = context.mock(AnswerGenerator.class); 
     final InputCollector inputCollector = context.mock(InputCollector.class);    
     final Guesser guesser = context.mock(Guesser.class);                         
     final OutputPrinter outputPrinter = context.mock(OutputPrinter.class);       
                                                                                  
     context.checking(new Expectations() {                                        
         {                                                                        
             one(answerGenerator).generate();                                     
             will(returnValue("1234"));                                           
         }                                                                        
                                                                                  
         {                                                                        
             one(inputCollector).guess();                                         
             will(returnValue("1234"));                                           
         }                                                                        
                                                                                  
         {                                                                        
             oneOf(guesser).verify(with(equal("1234")), with(equal("1234")));     
             will(returnValue("4A0B"));                                           
         }                                                                        
                                                                                  
         {                                                                        
             oneOf(outputPrinter).print(with(equal("You win")));                  
         }                                                                        
     });                                                                          
                                                                                  
     Game game = new Game(answerGenerator, inputCollector, guesser, outputPrinter);
     game.start();                                                                
                                                                                  
     context.assertIsSatisfied();                                                 
 }

  运行测试,会看到下面的错误信息:

java.lang.AssertionError: not all expectations were satisfied

expectations:
expected once, never invoked: answerGenerator.generate(); returns "1234"
expected once, never invoked: inputCollector.guess(); returns "1234"
expected once, never invoked: guesser.verify("1234"); returns "4A0B"
expected once, never invoked: outputPrinter.print("You win"); returns a default value
at org.jmock.lib.AssertionErrorTranslator.translate(AssertionErrorTranslator.java:20)
at org.jmock.Mockery.assertIsSatisfied(Mockery.java:196)
at com.swzhou.tdd.guess.number.GameFacts.should_play_game_and_win(GameFacts.java:54)
 
  太好了,正是我们期望的错误!别忘了我们只是在测试中定义了期望的游戏流程,真正的game.start()还是空的呢!现在让测试指引着我们前行吧。

  先改一改我们的Game类,把需要依赖的协作对象作为Game的字段:

private AnswerGenerator answerGenerator;
private InputCollector inputCollector;
private Guesser guesser;
private OutputPrinter outputPrinter;

public Game(AnswerGenerator answerGenerator, InputCollector inputCollector, Guesser guesser, OutputPrinter outputPrinter) {
     this.answerGenerator = answerGenerator;
     this.inputCollector = inputCollector;
     this.guesser = guesser;
     this.outputPrinter = outputPrinter;
}
 
  然后在start方法中通过answerGenerator来产生一个4位数:

public void start() {                         
    String answer = answerGenerator.generate();
}

  再跑测试,会发现仍然错,但结果有变化,第一步已经变绿了!

java.lang.AssertionError: not all expectations were satisfied
expectations:
expected once, already invoked 1 time: answerGenerator.generate(); returns "1234"
expected once, never invoked: inputCollector.guess(); returns "1234"
expected once, never invoked: guesser.verify("1234"); returns "4A0B"
expected once, never invoked: outputPrinter.print("You win"); returns a default value
at org.jmock.lib.AssertionErrorTranslator.translate(AssertionErrorTranslator.java:20)
at org.jmock.Mockery.assertIsSatisfied(Mockery.java:196)
at com.swzhou.tdd.guess.number.GameFacts.should_play_game_and_win(GameFacts.java:54)
 
  下面应该使用inputCollector来收集玩家的输入:

public void start() {                         
    String answer = answerGenerator.generate();
    String guess = inputCollector.guess();    
}

  跑测试,错但是结果进一步好转,已经有两步可以通过了:

 java.lang.AssertionError: not all expectations were satisfied
expectations:
expected once, already invoked 1 time: answerGenerator.generate(); returns "1234"
expected once, already invoked 1 time: inputCollector.guess(); returns "1234"
expected once, never invoked: guesser.verify("1234"); returns "4A0B"
expected once, never invoked: outputPrinter.print("You win"); returns a default value
at org.jmock.lib.AssertionErrorTranslator.translate(AssertionErrorTranslator.java:20)
at org.jmock.Mockery.assertIsSatisfied(Mockery.java:196)
at com.swzhou.tdd.guess.number.GameFacts.should_play_game_and_win(GameFacts.java:54)
 
  下面加快节奏,按照测试中的需求把剩下的流程走通吧:

public void start() {                         
    String answer = answerGenerator.generate();
    String guess = inputCollector.guess();    
    String result = "";                       
    do {                                      
       result = guesser.verify(guess, answer);
    } while (result != "4A0B");               
    outputPrinter.print("You win");           
}

  再跑测试,啊哈,终于看到那个久违的小绿条了!

  回顾一下这一轮从无到有、测试从红到绿的小迭代,我们终的产出是:

  1、一个可以用来描述游戏流程的测试(需求,文档?)。

  2、由该需求推出的一个流程骨架(Game.start)。

  3、一堆基于该骨架的协作类,虽然是空的,但它们每个的职责是清晰的。

  经过这艰难的第一步(实际上叙述的过程比较冗长,但反馈周期还是很快的),相信每个人都会对完整实现这个游戏建立信心,并且应该知道后面的步骤要怎么走了吧。是的,我们可以通过写更多的骨架测试来进一步完善它(比如考虑失败情况下的输出,增加对用户输入的验证等等),或者深入到每个小协作类中,继续以TDD的方式实现每一个协作类了。无论如何,骨架已在,我们是不大可能出现大的偏差了。