2013-03-13 96 views
3

我已经开始阅读Spring in Action书。Java代码测试中的澄清

我不知道JUnit,我认为我的疑问是关于。

有在这里笔者指的是,并说,这是很难测试的代码片段:

package com.springinaction.knights; 

public classDamselRescuingKnight implements Knight { 

    private RescueDamselQuest quest; 

    public DamselRescuingKnight() { 
     quest = new RescueDamselQuest(); 
    } 

    public voidembarkOnQuest() throwsQuestException { 
     quest.embark(); 
    } 
} 

笔者说:

这将会是非常难写一个单元测试DamselRescuingKnight。在这样的测试中,你希望能够断言在调用骑士的embarkOnQuest()时调用了任务的embark()方法。但是在这里没有明确的方法来完成。不幸的是,DamselRescuingKnight将保持未经测试。

作者对此有何意义?

为什么代码很难在这里测试?

回答

7

我最初的想法是,它很难测试,因为“RescureDamselQuest”对象在构造函数中初始化。这使得难以插入模拟对象。模拟对象将帮助您测试在“RescueDamselQuest”对象上调用了embark()方法。

一个更好的办法来解决这个可与有包括在构造函数的参数(通常我喜欢这种方法):

public DamselRescuingKnight(RescueDamselQuest quest){ 
    this.quest = quest; 
} 

或者添加setter:

public void setDamselRescuingKnight(RescueDamselQuest quest){ 
    this.quest = quest; 
} 
+1

你能简要描述一下模拟对象是什么,为什么我们在这里需要它来测试代码是否正常工作。 – 2013-03-13 13:54:18

+0

模拟对象是以受控方式模拟真实对象行为的模拟对象(http://en.wikipedia.org/wiki/Mock_object)。另外,我发现这篇文章对模拟vs存根对象非常有趣:http://martinfowler.com/articles/mocksArentStubs.html – 2013-03-13 17:57:10

0

这很难因为任务实现不能被换出。如果没有字节码修改,那么看看是否调用embark并不是一件简单的事情。

如果您可以在构造函数或设置器中设置任务实现,那么您可以传入一个实现,该实现可以侦听要启动的调用。

2

我给出的一个常见例子是考虑打开一个文件,解析它并获取一个数据类。大多数人会这样做:

Data openAndParse(String filename) { 
    ...openFile 
    ...parse 
} 

通过这样做,文件打开方法和解析是高度耦合和难以测试。如果您在打开和解析时遇到问题,那么解析还是打开?

通过编写JUnit测试,你是被迫,为了简单起见,做这样的事情......

BufferedReader openFile(String filename) { 
    ...open file and return reader 
} 

Data parse(BufferedReader input) { 
    ...parse and return data 
} 

的JUnit使我们更具凝聚力的解决方案。我们通过创建一个字符串,构造一个StringReader,然后一个BufferedReader来编写JUnit测试。那么猜猜怎么样?非常类似,我们现在可以使用解析来接受来自各种来源的输入,而不仅仅是文件。