打破局部依赖关系以unit testingvoid方法

我正在使用mockito进行练习,但我对如何测试依赖于本地对象中方法调用的方法有点困惑。 请参阅以下示例:

public class Worker { public void work() { Vodka vodka = new Vodka(); vodka.drink(); } } 

这个工人不喜欢做自己的工作,而是喜欢喝酒。 但是我想补充一个测试来certificate他在工作时喝酒。 但是没有办法这样做,因为我必须validation调用方法工作时调用方法drink()。 我想你同意我的意见,这是不可能测试的,所以我需要在开始测试之前打破依赖。 这是我的第一个疑问,你认为打破这种依赖的最佳方法是什么? 如果我只是将伏特加对象的范围更改为全局,我认为不会很好(我不想将它暴露给类的其他部分)。 我想创建一个工厂,像这样:

 public class Worker { private VodkaFactory vodkaFactory = new VodkaFactory(); public void work() { Vodka vodka = vodkaFactory.getVodka(); vodka.drink(); } } 

我不确定我是否确实打破了依赖关系,但我现在要做的是测试在执行work()时调用方法drink()。 我试了这个没有运气:

 @Test public void does_the_worker_drink_while_working () { VodkaFactory vodkaFactory = mock(VodkaFactory.class); Vodka vodka = mock(Vodka.class); Worker worker = new Worker(); worker.work(); when(vodkaFactory.getVodka()).thenReturn(vodka); verify(vodka,times(1)).drink(); } 

我嘲笑工厂,何时会发现工厂创建了一个新的伏特加对象。 但是当我想validation该方法调用方法drink()时,我会告诉我:

 Wanted but not invoked: vodka.drink(); -> at testing_void_methods_from_local_objects.WorkerSpecification.does_the_worker_drink_while_working(WorkerSpecification.java:22) Actually, there were zero interactions with this mock. 

我没有正确存根或者我做错了什么。 你能帮我完成一下这个测试,还能告诉我测试这些无法测试的方法的最佳方法是什么?

我知道mockito有一个名为doAnswer()的方法,它用于模拟方法调用,你觉得它在这种情况下有用吗? 我该怎么用?

更新:

我遵循建议在work()之前调用when() ,并且我也试图允许从类外部设置工厂:

 @Test public void does_the_worker_drink_while_working () { VodkaFactory vodkaFactory = mock(VodkaFactory.class); Vodka vodka = mock(Vodka.class); Worker worker = new Worker(); when(vodkaFactory.getVodka()).thenReturn(vodka); worker.work(); verify(vodka,times(1)).drink(); } 

现在这是生产代码:

 public class Worker { private VodkaFactory vodkaFactory; public void work() { Vodka vodka = vodkaFactory.getVodka(); vodka.drink(); } public void setVodkaFactory(VodkaFactory vodkaFactory) { this.vodkaFactory = vodkaFactory; } 

我得到的例外情况如下:

  java.lang.NullPointerException at testing_void_methods_called_from_local_objects.Worker.work(Worker.java:9) 

这是说vodka.drink()

抱歉,我仍然对这个问题感到困惑。

您的工人在这里创建自己的工厂类:

 private VodkaFactory vodkaFactory = new VodkaFactory(); 

您正在创建的模拟与工作器实例完全分离,因此缺少交互。 为了使其工作,工厂必须从“外部”注入工人,比如通过构造注入 。

如果这是遗留代码,您可以使用reflection将私有工厂实例替换为模拟实例。

正如JB Nizet在评论中所指出的,你的模拟设置是在已经调用work之后发生的。 为了使事情正确 ,请在调用任何使用它的代码之前注入mock并进行设置。

你需要设置你的伏特加工厂:

 @Test public void does_the_worker_drink_while_working() { VodkaFactory vodkaFactory = mock(VodkaFactory.class); Vodka vodka = mock(Vodka.class); Worker worker = new Worker(); when(vodkaFactory.getVodka()).thenReturn(vodka); //call your setter worker.setVodkaFactory(vodkaFactory); worker.work(); verify(vodka,times(1)).drink(); } 

这是一个评论而不是一个答案。 除了使工厂成为可注射的依赖项之外,你还可以确保训练你的模拟when(vodkaFactory.getVodka()).thenReturn(vodka); 在与之交互之前worker.work();

您尝试测试的代码中存在逻辑错误。 因为您已在Worker类中创建了VodkaFactory实例,而且您已将该字段VodkaFactory私有。

最好的解决方案是从类外部传递对VodkaFactory的引用。

 public class Worker { private VodkaFactory vodkaFactory; public void work() { Vodka vodka = vodkaFactory.getVodka(); vodka.drink(); } public void setVodkaFactory(VodkaFactory vf) { vodkaFactory = vf; } } 

现在,在@Test中,您可以使用setVodkaFactory setter传递VodkaFactory实例。

以下是一个完整的JMockitunit testing,它独立于Vodka依赖项的实现来运行Worker#work()方法:

 @Test public void workTest(@Mocked final Vodka mockBeverage) { new Worker().work(); new Verifications() {{ mockBeverage.drink(); times = 1; }}; }