当前位置: 首页 > 面试题库 >

Mockito:如何模拟JodaTime的界面

艾才良
2023-03-14
问题内容

我使用JodaTime#DateTime,并且需要模拟其行为。由于不可能直接模拟JodaTime#DateTime,因此我创建了一个接口

Clock.java

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

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依赖于当前时间相对于[4 PM-7PM]
EST时间间隔和托运人名称的时间。由于Shipper将通过参数传递,因此我们可以对其进行单元测试。但是我需要嘲笑时间。所以这是我尝试的

@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代码,请对我的代码非常批评。
我真的很想学


问题答案:

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

看一下SOLID原理以了解为什么这可能是个问题(尤其是在这种情况下是Dependency Inversion
Principle
)。如果您将JodaTime某个地方作为依赖项注入,那么在单元测试中,您可以根据需要用模拟,存根或间谍替换它的真实实例

但是:
JodaTime在生产环境中,无论使用寿命多长,这种东西极不可能注入其他任何东西。取而代之的是,在这种情况下,使用组合方法设计模式可能会更好。在这里,您将提取用于生成的任何计算/算法,并将其转换printjobName为另一种方法(由于您的代码段从未为该变量分配值,因此我在这里看不到如何做)。然后,您可以监视(部分模拟)您的被测类,以仅模拟该方法并返回固定值,而与JodaTime传递的实际日期时间无关,例如:

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
    }
}

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

@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);
}


 类似资料:
  • 我尝试测试一个发送jms消息的类,但无法模拟JmsTemplate JmsProducer.class: JmsProducerTest。类别: 当我运行这个测试用例时,它给了我:java。lang.IllegalArgumentException:对象不是声明类的实例 你对这个问题有什么想法吗?

  • 是模拟抽象类:,而是接口。这是失败点: 如何模拟这段代码?

  • 我的测试存根是 我在这里做错了什么?

  • 问题内容: 我有一个建设者: 在模仿器中模拟生成器将使我对每种方法都无效。因此,有没有一种简单的方法可以使生成器在每次调用函数时返回自身,而无需使用来模拟每个函数本身。 问题答案: 您可以使用RETURN_DEEP_STUBS模拟链接API。 如果您知道将调用您的构建器的确切顺序,那么以下是如何使用它的示例: 不幸的是,这不会为您提供一种模拟“所有各种生成器方法”的通用方法,以使它们始终返回此值,

  • 我想模拟注释来检查根据给定注释返回结果的类的良好行为。 Erg.mockito.exceptions.misusing.WurnTypeOfReturnValue:annotationType()不能返回KClassImpl annotationType()应该返回Class 如果你不确定为什么你会超过错误,请继续阅读。由于语法的性质,上面的问题可能会发生,因为: 此异常可能发生在错误编写的多线程

  • 问题内容: 当我创建一个说类Employee的模拟对象时。它不调用Employee对象的构造函数。我知道Mockito在内部使用CGLIb和反射,创建了一个代理类,将该类扩展为模拟。如果未调用employee的构造函数,那么如何创建employee类的模拟实例? 问题答案: Mockito使用CGLib生成类对象。但是,要实例化此类对象,它使用Objenesis http://objenesis.