我正在使用mockito进行练习,但我对如何测试依赖于本地对象中方法调用的方法感到困惑。 请参阅以下示例:
public class Worker {
public void work() {
Vodka vodka = new Vodka();
vodka.drink();
}
}
这名工人不喜欢自己的工作,而是喜欢喝酒。但是我想补充一个测试来证明他在工作时喝酒。但是没有办法这样做,因为我必须验证调用方法工作时调用方法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();
}
我嘲笑工厂,何时会检测到工厂创建了一个新的伏特加对象。但是当我想验证该方法调用方法drink()的时候,mockito告诉我:
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()的方法用于模拟方法调用,你认为它在这种情况下有用吗? 我应该如何使用它?
更新
我正在遵循建议,以便在when()
之前调用work()
,同时我也尝试允许在课堂外设置工厂:
@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()
很抱歉,我仍然对这个问题感到困惑。
答案 0 :(得分:3)
你的工人在这里创建了自己的工厂类:
private VodkaFactory vodkaFactory = new VodkaFactory();
您正在创建的模拟与工作者实例完全分离,因此缺乏交互。为了使其工作,工厂必须从外部注入工人",通过constructor injection说明。
如果这是遗留代码,您可以使用反射将私有工厂实例替换为模拟代码。
如JB Nizet在评论中所述,您的模拟设置在调用work
之后出现。为了使事情正确,请在调用任何使用它的代码之前注入模拟并进行设置。
答案 1 :(得分:3)
你需要设置你的伏特加工厂:
@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();
}
答案 2 :(得分:1)
您尝试测试的代码中存在逻辑错误。因为您已在VodkaFactory
类中创建了Worker
实例,而且您已将该字段设为私有。
最好的解决方案是从课堂外传递对VodkaFactory
的引用。
public class Worker {
private VodkaFactory vodkaFactory;
public void work() {
Vodka vodka = vodkaFactory.getVodka();
vodka.drink();
}
public void setVodkaFactory(VodkaFactory vf) {
vodkaFactory = vf;
}
}
现在,在@Test中,您可以使用VodkaFactory
setter传递模拟的setVodkaFactory
实例。
答案 3 :(得分:1)
这是一个评论而不是一个答案。除了使工厂成为可注射依赖项之外,您还可以确保在与模拟when(vodkaFactory.getVodka()).thenReturn(vodka);
交互之前训练模拟worker.work();
答案 4 :(得分:1)
以下是一个完整的JMockit单元测试,它与Worker#work()
依赖项的实现隔离开来运行Vodka
方法:
@Test
public void workTest(@Mocked final Vodka mockBeverage)
{
new Worker().work();
new Verifications() {{ mockBeverage.drink(); times = 1; }};
}