我已经开始阅读Spring in Action一书了。
我不了解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仍未经过测试。
作者的意思是什么?
为什么这里的代码难以测试?
答案 0 :(得分:8)
我最初的想法是很难测试,因为“RescureDamselQuest”对象在构造函数中初始化。这使得例如插入模拟对象变得困难。模拟对象可以帮助您测试在“RescueDamselQuest”对象上调用embark()方法。
解决这个问题的更好方法是在构造函数中包含一个参数(通常我更喜欢这个方法):
public DamselRescuingKnight(RescueDamselQuest quest){
this.quest = quest;
}
或者添加一个setter:
public void setDamselRescuingKnight(RescueDamselQuest quest){
this.quest = quest;
}
答案 1 :(得分: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测试。好吧猜怎么着?同样,我们现在可以使用parse接受来自各种来源的输入,而不仅仅是文件。
答案 2 :(得分:0)
测试很困难,因为无法换出任务实现。如果没有字节码修改,就没有简单的方法来查看是否调用了embark。
如果你可以在构造函数或setter中设置任务实现,你可以传入一个可以监视embark调用的实现。
答案 3 :(得分:0)
需要增加字段的可访问性和要测试的类的方法。例如,如果正在测试一种包私有的方法(默认),则通常位于不同包中的测试用例将无法测试此方法。因此,建议更改字段的可访问性以测试该方法。通过将 RescueDamselQuest 字段的可访问性从私有更改为默认,可以测试未使用DI的 DamselRescuingKnight 类。然后使用模仿编写测试用例。这是测试用例的代码
@Test
public void knightShouldEmbarkOnQuest() throws QuestException {
DamselRescuingKnight knight = new DamselRescuingKnight();
RescueDamselQuest quest = mock(RescueDamselQuest.class);
knight.quest = quest;
knight.embarkOnQuest();
verify(quest, times(1)).embark();
}
并且在DamselRescuingKnight类中进行了更改以删除私有辅助功能的行
RescueDamselQuest quest;