开发者

Easymock using date expectation

开发者 https://www.devze.com 2022-12-18 13:42 出处:网络
I\'m mocking a method with easymock that has a date in its body, something like this: public void testedMethod() {

I'm mocking a method with easymock that has a date in its body, something like this:

public void testedMethod() {
    ...
    if (doSomething(new Date())) {
    ...
}

And my test looks like this:

public void testThatMethod() {
    ...
    expect(testedClass.testedMethod(new Date())).andReturn(false);
    ...
}

But when I run the test sometimes I get an error like this:

Unexpected method call testedMethod(Thu Jan 28 09:45:13 GMT-03:00 2010): testedMet开发者_开发问答hod(Thu Jan 28 09:45:13 GMT-03:00 2010): expected: 1, actual: 0

I think that it's because sometimes the date has a slightly difference. I've tried the some flexible expectations without success. Is there a way to get around this?


We're constantly facing similar issues and these are the alternatives I see:

  1. Provide the date as a parameter to the method (+) Quick change (-) a bit dirty - when you just want to use "now", it also pollutes your interface
  2. Pull the date in from a collaborator "QueryCurrentDateProvider" (+) Still pretty quick (+) Can also be mocked -> you are sure you use the same date (-) unnecessary collaborators created for every service where you need to do something similar
  3. Write your own EasyMock argument matcher where you abstract to what you actually want to do - when you're just interested in the day, not the time you can use something like commons DateUtils.isSameDay to get this to run (+) cleanest solution (+) no change to your productive code (-) you have to write your own matcher (although I don't understand why EasyMock doesn't already have that)
  4. Move the "new Date()" to a private method, then mock this method with something like PowerMock (+) quick (+) small change to the productive code (-) introduce power mock as a dependency
  5. Change the parameter from Date to String and use a common pattern to transform the date to a string prior to calling the method (+) quick (+) no additional code, libraries required on the testing site (-) you have to format the date before calling the method and parse the date in the called method

So it really comes up to your personal liking. When you're working with current timestamps a lot I would recommend the argument matcher - since this investment will pay off quickly.


Stop using new Date(), use a Calendar with constant time instead.

//Declare the Calendar in your test method
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(0l);

//receive the calendar to be used in testedClass constructor
public void testedMethod() {
    ...
    if (doSomething(cal.getTime())) {
    ...
}

//use the same calendar to make the assertion
public void testThatMethod() {
    ...
    expect(testedClass.(testedMethod(cal.getTime())).andReturn(false);
    ...
}


I just found this thread and it helped me solve a problem I was stuck at for a good hour.

Thought I'd share my 2 cents:

If you don't care about the date value and just want to know that it IS a Date object, just use EasyMock's predefined matcher:

EasyMock.expect(objectMock.isPollingTimeOut(EasyMock.eq(600000L), EasyMock.isA(Date.class), EasyMock.eq(someMock))).andReturn(false);

Remember, once you use a matcher, you've to use matchers for all arguments in the method you're testing like what I did.


If you can figure out exactly why it's failing, you can write your own matcher to be more flexible in matching the date. See the section on matchers http://easymock.org/EasyMock2_2_Documentation.html


It could be that the millisecond part of the dates are different. You probably need to zero that using Calendar.set() before you create the date object:

Calendar myCalendar = Calendar.getInstance();
myCalendar.set(Calendar.MILLISECOND, 0);
Date testDate = myCalendar.getTime();

But that is a guess :)


Use a custom EasyMock Matcher for a loose date comparison. Here is an example that will check if the dates are within one second of each other.

public class MyEasyMockMatchers {

    public static Date withinSecondOf(Date value){
        EasyMock.reportMatcher(new IArgumentMatcher() {
            @Override
            public boolean matches(Object argument) {
                return argument instanceof Date
                        && Math.abs(((Date) argument).getTime() - value.getTime()) < Timer.ONE_SECOND;
            }

            @Override
            public void appendTo(StringBuffer buffer) {
                buffer.append(value.toString());
            }
        });
        return null;
    }
}

Then in your test use:

import static my.package.MyEasyMockMatchers.*;
...
expect(testedClass.testedMethod(withinSecondOf(new Date())))
        .andReturn(false);

Note that when you use one matcher, you have to use matchers for the entire expect. For example:

expect(testedClass.testedMethod(42, "hello", new Date()))
        .andReturn(false);
// becomes
expect(testedClass.testedMethod(eq(42), eq("hello"), withinSecondOf(new Date())))
        .andReturn(false);

See: https://easymock.org/user-guide.html#verification-expectations

0

精彩评论

暂无评论...
验证码 换一张
取 消