Mockito:如何模拟 JodaTime 的接口 [英] Mockito: How to mock an interface of JodaTime

查看:23
本文介绍了Mockito:如何模拟 JodaTime 的接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 JodaTime#DateTime,我需要模拟它的行为.由于无法直接模拟JodaTime#DateTime,我创建了它的接口

I use JodaTime#DateTime, and I need to mock its behavior. Since it is not possible to directly mock JodaTime#DateTime, I create an interface of it

时钟.java

public interface Clock {
    DateTime getCurrentDateTimeEST();
    DateTime getFourPM_EST();
    DateTime getSevenPM_EST();
}

JodaTime.java

JodaTime.java

public class JodaTime implements Clock {

    @Override
    public DateTime getCurrentDateTimeEST() {
        return new DateTime(DateTimeZone.forID("EST"));
    }

    @Override
    public DateTime getFourPM_EST() {
        DateTime current = getCurrentDateTimeEST();
        return new DateTime(current.getYear(), current.getMonthOfYear(), 
                current.getDayOfMonth(), 16, 0, 0, 0, DateTimeZone.forID("EST"));
    }

    @Override
    public DateTime getSevenPM_EST() {
        DateTime current = getCurrentDateTimeEST();
        return new DateTime(current.getYear(), current.getMonthOfYear(), 
                current.getDayOfMonth(), 19, 0, 0, 0, DateTimeZone.forID("EST")); 
    }   
}

这是我要测试的方法

public class PrintProcessor{

  Clock jodaTime;

  public PrintProcessor(){
      jodaTime = new JodaTime();
  }
  ...
  public String getPrintJobName(Shipper shipper){
    String printJobName = null;
    //Get current EST time
    if(jodaTime.getCurrentDateTimeEST().isBefore(jodaTime.getFourPM_EST()) ||
            jodaTime.getCurrentDateTimeEST().isAfter(jodaTime.getSevenPM_EST())){   //Before 4PM EST and after 7PM EST
        switch(shipper){
        case X:
        ...
    }else if(jodaTime.getCurrentDateTimeEST().isBefore(jodaTime.getSevenPM_EST())){ //Between 4PM-7PM EST
        switch(shipper){
        case X:
        ... 
    }
    return printJobName;
  }
}

如您所见,printJobName 取决于相对于美国东部时间 [4PM-7PM] 时间间隔的当前时间和发货人名称.由于 Shipper 将通过参数传递,我们可以对其进行单元测试,没有问题.但我需要嘲笑时间.所以这就是我尝试的

As you can see the printJobName depend on the current time of the day relative to the time interval [4PM-7PM] EST and the Shipper name. Since Shipper will be pass via parameter, we can unit test it no problem. But I need to mock the time. So here is what I try

@Test
public void testGetPrintJobNameBeforeFourPM(){
    DateTime current = new DateTime(DateTimeZone.forID("EST"));
    Clock clock = mock(Clock.class);
    //Always return 6pm when I try to ask for the current time
    when(clock.getCurrentDateTimeEST()).thenReturn(new DateTime(current.getYear(), current.getMonthOfYear(), 
            current.getDayOfMonth(), 18, 0, 0, 0, DateTimeZone.forID("EST")));
    //Test for Fedex
    String printJobName = printProcessor.getPrintJobName(Shipper.X);
    assertEquals("XNCRMNCF", printJobName);
}

测试应该会失败,因为我在下午 6 点通过,但 XNCRMNCF 是下午 4 点之前的名称.我是否还需要模拟 printProcessor .如果我所拥有的是错误的.我该如何解决?我正在努力学习编写高级java代码,请批评我的代码.我真的很想学习

The test should fail since I pass in 6PM, but XNCRMNCF is the name for before 4PM. Do I need to mock printProcessor as well. If what I have is wrong. How should I fix it? I am trying to learn writing high level java code, please be very criticized about my code. I really want to learn

推荐答案

这是一个典型的测试案例,显示了设计中的潜在缺陷.您不能模拟 JodaTime,因为您在被测类中有对这些类的硬连线依赖.

This is a classic case of testing showing up a potential flaw in design. You cannot mock JodaTime because you have a hard-wired dependency to these classes in your class-under-test.

查看 SOLID 原则,了解为什么这可以是一个问题(特别是在这种情况下依赖倒置原则).如果您将 JodaTime 作为依赖项注入某处,那么在您的单元测试中,您将能够用 模拟、存根或间谍(视情况而定).

Have a look at the SOLID principles to understand why this could be a problem (especially in this case the Dependency Inversion Principle). If you injected JodaTime somewhere as a dependency, then in your unit test you would be able to replace a real instace of it with a mock, stub or spy as appropriate.

然而: JodaTime 是极不可能在生产环境中注入任何其他东西的东西,无论它存在多长时间.相反,在这种情况下,您可能会更好地使用 组合方法设计图案.在这里,您可以将用于生成 printjobName 的任何计算/算法提取到另一种方法(我在这里看不到您是如何做到的,因为您的代码片段从未为该变量分配值).然后你可以监视(部分模拟)你的被测类只模拟该方法并返回一个固定值,而不管 JodaTime 提供的实际日期时间,例如:

However: JodaTime is something that is highly unlikely to be injected with anything else in the production environment, no matter how long it is live for. Instead, in this case you would probably be better served with the Composed Method Design Pattern. Here, you would extract whatever calculation/algorithm you use to generate the printjobName to another method (I can't see how you do it here because your code snippet never assigns a value to that variable). Then you can spy (partial mock) your class under test to only mock that method and return a fixed value, regardless of the real date time that JodaTime is delivering, for instance:

public class PrintProcessor {
    ...
    public String getPrintJobName(Shipper shipper) {
        String printJobName = null;
        String timeHash = this.getTimeHash();
        if (this.isBeforeFourPM()) {
            switch(shipper) {
                printJobName = // Do something with timeHash to generate name
            }
        } else {
            ...
        }
        return printJobName;
    }

    public boolean isBeforeFourPM() {
        return (jodaTime.getCurrentDateTimeEST().isBefore(jodaTime.getFourPM_EST()) ||
            jodaTime.getCurrentDateTimeEST().isAfter(jodaTime.getSevenPM_EST()));
    }

    public String getTimeHash() {
        ... // Do something to hash the time value in to a String
    }
}

现在您可以在测试中编写:

Now you can write in your test:

@Test
public void testGetPrintJobNameBeforeFourPM() {
    PrintProcessor concretePrintProcessor = new PrintProcessor();
    PrintProcessor printProcessor = spy(concretePrintProcessor);
    doReturn(true).when(printProcessor).isBeforeFourPM();

    String printJobName = printProcessor.getPrintJobName(Shipper.X);

    assertEquals("XNCRMNCF", printJobName);
}

这篇关于Mockito:如何模拟 JodaTime 的接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆