3

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

public class Worker {          

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

这个工人,他不做他的工作,他喜欢喝酒。但是我想添加一个测试来证明他在工作时喝酒。但是没有办法这样做,因为我必须在调用方法 work 时验证方法 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();
    }

我模拟了工厂,何时会检测到工厂创建了一个新的伏特加对象。但是当我验证该方法调用了 1 次方法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() 的方法,它用于模拟方法调用,你认为它在这种情况下有用吗?我应该如何使用它?

更新:

我正在按照建议在 thewhen()之前获取调用,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()

抱歉,我仍然对问题所在感到困惑。

4

5 回答 5

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();
}
于 2013-03-12T02:28:06.400 回答
3

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

private VodkaFactory vodkaFactory = new VodkaFactory();

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

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

正如 JB Nizet 在评论中指出的那样,您的模拟设置在work已经调用之后。为了使事情正确,请在调用任何使用它的代码之前注入模拟并设置它。

于 2013-03-11T23:02:39.553 回答
1

与其说是答案,不如说是评论。除了使 factory 成为可注入的依赖项之外,您还可以确保在与 mockwhen(vodkaFactory.getVodka()).thenReturn(vodka);交互之前对其进行训练worker.work();

于 2013-03-11T23:07:03.440 回答
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 中,您可以使用setter传递您的模拟VodkaFactory实例。setVodkaFactory

于 2013-03-11T23:03:01.290 回答
1

以下是一个完整的JMockit单元测试,它独立Worker#work()于其Vodka依赖项的实现来执行该方法:

@Test
public void workTest(@Mocked final Vodka mockBeverage)
{
    new Worker().work();

    new Verifications() {{ mockBeverage.drink(); times = 1; }};
}
于 2013-03-12T20:14:31.693 回答