简体   繁体   English

Java Mockito何时返回对象创建

[英]Java Mockito when-return on Object creation

I'm trying to test a class that calculates age. 我正在尝试测试计算年龄的课程。 The method that calculates the age looks like this: 计算年龄的方法如下所示:

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

Since I want the JUnit to be time-independent I want the today variable to always be January 1, 2016. To do this I tried going the Mockito.when route but am running into trouble. 因为我希望JUnit与时间无关,所以我希望today变量始终为2016年1月1日。为此,我尝试使用Mockito.when路线,但遇到了麻烦。

I first had this: 我首先有这个:

public class CalculatorTest {

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

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

But to that I got this error: 但是到此我得到了这个错误:

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.

So then I tried to make a method inside the Calculator class to return the current date like so: 因此,我尝试在Calculator类中创建一个方法以返回当前日期,如下所示:

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

So that I could do this: 这样我就可以做到这一点:

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

But to that I get the exact same problem. 但是,我得到了完全相同的问题。 So any ideas on how to return a predefined localdate object whenever the age calculation is triggered? 那么,关于何时触发年龄计算如何返回预定义的本地日期对象的任何想法?

Instead of mocking, I'd suggest using Joda's DateTimeUtils to "freeze" the time. 我建议不要使用模拟,而是使用Joda的DateTimeUtils “冻结”时间。 You would also need to use org.joda.time.LocalDate instead of java.time.LocalDate in your application code. 您还需要在应用程序代码中使用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();
    }
}

For pure Java, consider some approaches described here , particularly, injecting a Clock or using PowerMock. 对于纯Java,请考虑此处介绍的一些方法,特别是注入Clock或使用PowerMock。

Injecting a Clock is quite similar to the Joda example; 注入Clock与Joda示例非常相似。 you just need to maintain your own static Clock . 您只需要维护自己的静态Clock Your application code would look like this: 您的应用程序代码如下所示:

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

And the test would freeze the time like this: 测试会冻结这样的时间:

public class CalculatorTest {

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

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

I suggest you to trick to test your method: 我建议您欺骗以测试您的方法:

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

I allways use my own TimeFactory to retrieve the currentdate in my application. 我总是使用自己的TimeFactory来检索应用程序中的currentdate。 This way I am flexible on manipulating it (during JUnit testing). 这样,我可以灵活地进行操作(在JUnit测试期间)。 This is what the TimeFactory looks like: 这是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;
    }
}

Using this TimeFactory I can easily do Time travelling: 使用此TimeFactory,我可以轻松地进行时间旅行:

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

You can expand this code to fix the time. 您可以扩展此代码以固定时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM