首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mockito:如何模拟JodaTime的接口

Mockito:如何模拟JodaTime的接口
EN

Stack Overflow用户
提问于 2011-05-18 19:05:26
回答 3查看 12.4K关注 0票数 11

我使用JodaTime#DateTime,我需要嘲笑它的行为。由于不可能直接模拟JodaTime#DateTime,所以我创建了它的接口

Clock.java

代码语言:javascript
复制
public interface Clock {
    DateTime getCurrentDateTimeEST();
    DateTime getFourPM_EST();
    DateTime getSevenPM_EST();
}

JodaTime.java

代码语言:javascript
复制
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")); 
    }   
}

下面是我要测试的方法

代码语言:javascript
复制
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取决于一天中的当前时间,相对于EST的时间间隔4PM-7PM和Shipper名称。由于托运人将通过参数,我们可以单位测试它没有问题。但我需要嘲笑时间。以下是我所尝试的

代码语言:javascript
复制
@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代码,请对我的代码提出批评。我真的很想学

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-05-19 15:10:33

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

查看一下固体原理,了解为什么这可能是一个问题(尤其是在本例中是依赖反演原理)。如果您将JodaTime作为依赖项注入某个地方,那么在单元测试中,您将能够适当地用嘲弄、存根或间谍替换它的真实实例。

然而, JodaTime是一种非常不可能在生产环境中注入其他任何东西的东西,不管它的寿命有多长。相反,在这种情况下,您可能会得到更好的组合方法设计模式服务。在这里,您将提取用于将printjobName生成到另一个方法的任何计算/算法(我无法在这里看到您是如何做到的,因为您的代码片段从未为该变量分配值)。然后,您可以监视(部分模拟)被测试的类,以便只模拟该方法并返回一个固定值,而不考虑JodaTime交付的实际日期,例如:

代码语言:javascript
复制
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
    }
}

现在你可以在你的测试中写:

代码语言:javascript
复制
@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);
}
票数 5
EN

Stack Overflow用户

发布于 2011-05-19 14:06:02

你永远不会给PrintProcessor你的嘲弄。对对象进行模拟与对对象进行模拟是不同的。因此,当您在PrintProcessor上调用方法时,它是在JodaTime的一个实际实例上操作的。有几种方法可以为PrintProcessor提供模拟:

  1. 使用PowerMockito (确保您使用的是PowerMock-mockito而不是PowerMock-easymock ),并模拟JodaTime构造函数以返回您模拟的Clock对象whenNew(JodaTime.class).withNoArguments().thenReturn(mockJodaTime);,这将在使用JodaTime的无arg构造函数时插入您的模拟。注意:这将要求您使用JodaTime类的模拟。
  2. Clock jodaTime字段添加一个setter方法(如果仅在构造函数中定义该方法,则可能应该是final)。
  3. 为您的Clock类使用一个工厂,只需在测试期间返回模拟(您可以使用PowerMockito来模拟静态方法)。
  4. 创建一个带有Clock参数的构造函数并传入模拟。
票数 3
EN

Stack Overflow用户

发布于 2011-05-18 21:06:15

我觉得你绝对走对了路。创建用于模拟的时钟接口绝对是个好主意。

我在您的代码中没有看到一件事:将模拟的时钟注入printProcessor。在创建模拟之后,我认为您需要类似于以下内容的内容:

代码语言:javascript
复制
printProcessor.setClock(clock)

(这是在你打电话给getPrintJobName之前。此设置程序应在您的jodaTime类中设置PrintProcessor属性)

我已经习惯了EasyMock,所以我可能错了,但我很肯定您还需要为getFourPM_EST和getSevenPM_EST调用设定期望。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6049777

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档