2016-01-05 154 views
1

我试图测试一个计算年龄的类。计算年龄的方法是这样的:Java Mockito何时返回创建对象

public static int getAge(LocalDate birthdate) { 
    LocalDate today = new LocalDate(); 
    Period period = new Period(birthdate, today, PeriodType.yearMonthDay()); 
    return period.getYears(); 
} 

因为我想了JUnit是时间无关我想today变量始终是2016年1月1日,为了做到这一点,我试图去了Mockito.when路线,但我遇到了麻烦。

我第一次有这样的:

public class CalculatorTest { 

    @Before 
    public void setUp() throws Exception { 
     LocalDate today = new LocalDate(2016,1,1); 

     Mockito.when(new LocalDate()).thenReturn(today); 
    } 
} 

但是到我得到这个错误:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'. 
For example: 
    when(mock.getArticles()).thenReturn(articles); 

Also, this error might show up because: 
1. you stub either of: final/private/equals()/hashCode() methods. 
    Those methods *cannot* be stubbed/verified. 
    Mocking methods declared on non-public parent classes is not supported. 
2. inside when() you don't call method on mock but on some other object. 

于是我试图让计算器类中的方法返回当前的日期,像这样:

public static LocalDate getCurrentDate() { 
    return new LocalDate(); 
} 

public static int getAge(LocalDate birthdate) { 
    LocalDate today = getCurrentDate(); 
    Period period = new Period(birthdate, today, PeriodType.yearMonthDay()); 
    return period.getYears(); 
} 

,这样我可以做到这一点:

public class CalculatorTest { 

    @Before 
    public void setUp() throws Exception { 
     CalculatorTest mock = Mockito.mock(CalculatorTest.class); 
     LocalDate today = new LocalDate(2016,1,1); 

     Mockito.when(mock.getCurrentDate()).thenReturn(today); 
    } 
} 

但是,我得到了完全相同的问题。那么,有关如何在触发年龄计算时返回预定义的本地对象的任何想法?

+2

你试过从'getCurrentDate'删除'static'修改? Mockito不能模拟静态方法。 – user3707125

回答

4

而不是嘲笑,我建议使用乔达的DateTimeUtils来“冻结”时间。您还需要在应用程序代码中使用org.joda.time.LocalDate而不是java.time.LocalDate

public class CalculatorTest { 

    @Before 
    public void setUp() throws Exception { 
     DateTimeUtils.setCurrentMillisFixed(new LocalDate(2016,1,1).toDateTimeAtStartOfDay().getMillis()); 
    } 

    @After 
    public void tearDown() { 
     DateTimeUtils.setCurrentMillisSystem(); 
    } 
} 

对于纯Java,可以考虑一些方法描述here,具体地,注入Clock或使用PowerMock。

注入Clock与Joda示例非常相似;你只需要保持自己的静态Clock。您的应用程序代码应该是这样的:

static Clock appClock = Clock.systemDefaultZone(); 

public static int getAge(LocalDate birthdate) { 
    LocalDate today = LocalDate.now(appClock); 
    Period period = new Period(birthdate, today, PeriodType.yearMonthDay()); 
    return period.getYears(); 
} 

并且测试将冻结这样的时候:

public class CalculatorTest { 

    @Before 
    public void setUp() { 
     appClock = Clock.fixed(LocalDate(2016,1,1).toDateTimeAtStartOfDay(), ZoneId.systemDefault()); 
    } 

    @After 
    public void tearDown() { 
     appClock = Clock.systemDefaultZone(); 
    } 
} 
+0

DateMidnight已弃用,我应该使用LocalDate吗? – Richard

+0

@Richard你是否想过[toDateTimeAtMidnight](http://joda-time.sourceforge.net/apidocs/org/joda/time/LocalDate.html#toDateTimeAtMidnight())? –

+0

不,我试过'DateMidnight'和intelliJ说它的弃用。 – Richard

0

我八方通用我自己的TimeFactory来检索我的应用程序的currentdate。这样我可以灵活地操作它(在JUnit测试期间)。这就是TimeFactory样子:

public final class TimeFactory { 

    static int offsetAmount = 0; 
    static TemporalUnit offsetUnit = ChronoUnit.MILLIS; 

    public static LocalDateTime getLocalDateTimeNow() { 
     return LocalDateTime.now().plus(offsetAmount, offsetUnit); 
    } 

    public static void reset() { 
     offsetAmount = 0; 
    } 

    public static void travelToPast(int amount, TemporalUnit unit) { 
     offsetAmount = amount * -1; 
     offsetUnit = unit; 
    } 

    public static void travelToFuture(int amount, TemporalUnit unit) { 
     offsetAmount = amount; 
     offsetUnit = unit; 
    } 
} 

使用这个TimeFactory我可以很容易做到时间旅行:

// start test 
TimeFactory.travelToPast(10, ChronoUnit.HOURS); 
// continue tests 
TimeFactory.reset(); 
// check that things have happend in the past. 

可以扩大这个代码来解决的时间。

1

我建议你欺骗到测试方法:

public static int getAge(LocalDate currDate, LocalDate birthdate) { 
    Period period = new Period(birthdate, currDate, PeriodType.yearMonthDay()); 
    return period.getYears(); 
} 

public static int getAge(LocalDate birthdate) { 
    return getAge(new LocalDate(), birthdate); 
}