没有依赖注入的单元测试

时间:2016-03-20 21:27:58

标签: java spring unit-testing junit dependency-injection

Spring in Action中的代码:

public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;
    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}

public class BraveKnight implements Knight {
    private Quest quest;
    public BraveKnight(Quest quest) {
        this.quest = quest;
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}


public class BraveKnightTest {
    @Test
    public void knightShouldEmbarkOnQuest() {
        Quest mockQuest = mock(Quest.class);
        BraveKnight knight = new BraveKnight(mockQuest);
        knight.embarkOnQuest();
        verify(mockQuest, times(1)).embark();
    }
}

我理解依赖注入的使用,它允许我们在不修改依赖代码的情况下切换实现。

这本书说“编写单元测试非常困难......”。

但是,我无法理解如何在没有依赖注入的情况下进行单元测试非常困难!我的直觉拒绝合作!

您是否可以开始为“DamselRescuingKnight”类和任何其他更好的示例类(没有DI)编写junit / unit测试,以使我意识到DI使单元测试更容易的点/阶段?

3 个答案:

答案 0 :(得分:3)

当您尝试测试git remote add REMOTE URL时,上述示例中存在困难。假设您要测试那个(见下文)

DamselRescuingKnight

你怎么能确定knight.embarkOnQuest()确实做了什么?答案是你不能,因为你无法访问它在内部使用的任务实例。

现在,为了能够测试这样的类,您可以向Knight添加public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; public DamselRescuingKnight() { this.quest = new RescueDamselQuest(); } public void embarkOnQuest() { quest.embark(); } } public class DamselRescuingKnight Test { @Test public void knightShouldEmbarkOnQuest() { DamselRescuingKnight knight = new DamselRescuingKnight (); knight.embarkOnQuest(); // now what? } } 方法,然后还向Quest添加getQuest()方法。 这也很公平地说,这个例子非常简单,因为骑士只调用没有参数的任务,而没有别的。如果骑士会与任务相互作用并从铁匠那里得到一些武器,那么你也会以某种方式允许其进入。你可以做所有的样板来完成它。但是假设,你将参数传递给铁匠 - 你如何确保传递的参数是正确的?或者你如何确保骑士在进入任务之前获得他的武器

这是依赖注入来拯救的地方。你可以创建模拟(通过使用模拟框架,或通过实现自己的模拟),以便你可以验证你的骑士是否做了预期的事情。

答案 1 :(得分:1)

问题当然是 while($rows = $stmt->fetch(PDO::FETCH_ASSOC)){ ?> <tr> <td border="0" type="hidden" style="display:none;"><input type="hidden" name="hidden" value=<?php echo $rows['member_id']; ?></td> <td class="center"><?php echo $rows['username']; ?></td> <td class="center"><?php echo $rows['email']; ?></td> <?PHP if($_SESSION['user_group'] == 63){ echo '<td class="center"><input type="radio" id='?><?php echo $rows['username']; 'name="gp" value="system admin"' ?> <?php echo ($rows['permission']== 31 )?'checked':'' ?>'</input> </td>' <?PHP ; echo '<td class="center"><input type="radio" id='?><?php echo $rows['username']; 'name="gp" value="admin"' ?> <?php echo($rows['permission']== 15 )?'checked':'' ?> '</input> </td>' <?PHP ; echo '<td class="center"><input type="radio" id='?><?php echo $rows['username'];'name="gp" value="user"' ?><?php echo ($rows['permission']== 1 )?'checked':'' ?> '</input> </td>' <?PHP ; } elseif($_SESSION['user_group'] == 31){ echo '<td class="center"><input type="radio" id='?><?php echo $rows['username']; 'name="gp" value="admin"' ?> <?php echo($rows['permission']== 15 )?'checked':'' ?> '</input> </td>' <?PHP ; echo '<td class="center"><input type="radio" id='?><?php echo $rows['username'];'name="gp" value="user"' ?><?php echo ($rows['permission']== 1 )?'checked':'' ?> '</input> </td>' <?PHP ; } elseif($_SESSION['user_group'] == 15){ echo '<td class="center"><input type="radio" id='?><?php echo $rows['username'];'name="gp" value="user"' ?><?php echo ($rows['permission']== 1 )?'checked':'' ?> '</input> </td>' <?PHP ; }?> <td class="center"><button name="update" type="update">submit</button></td> </tr> <?php }变量。您想以某种方式检查是否调用了quest方法。如果不能用模拟实例替换它,这非常困难。

如果变量是embark()而不是protected,则测试用例可能会因为生活在同一个包中而覆盖它。

您还可以使用面向方面编程来替换变量。

但最简单的方法是,如果代码是从一开始就使用依赖注入编写的。

您想知道如何使用AOP。以下是一个AspectJ切入点的示例,您可以在单元测试中将private实例替换为一个名为RescueDamselQuest的模拟实例(如果我不能使语法完全正确,请为此道歉) ,自从我使用AspectJ以来已经有一段时间了:

MockRescueDamselQuest

这将捕获aspect MockRescueDamselQuestInstantiations { RescueDamselQuest around (): call(RescueDamselQuest.new()) { return new MockRescueDamselQuest(); } } 的任何实例化(即调用RescueDamselQuest)并返回new RescueDamselQuest()对象。

考虑到这需要多少布线,我强烈建议使用依赖注入!

答案 2 :(得分:1)

当我在《 Spring in Action》中阅读本文时,这也使我感到困惑。阅读以上答案后,我想补充一点,当不使用DI时,Junit方法需要调用私有对象(可访问)的方法,并且该对象Quest是在DamselRescuingKnight的构造函数中创建的,因此embarkQuest()的测试用例可以不被写。相反,在使用DI时,您将外部化对象创建,而Junit方法可以创建该对象,以便可以对其进行访问,然后可以测试emabarkQuest(),而最终需要测试quest方法。