java: how to mock Calendar.getInstance()?
Asked Answered
T

9

36

In my code I have something like this:

private void doSomething() {
   Calendar today = Calendar.getInstance();
   ....
}

How can I "mock" it in my junit test to return a specific date?

Thump answered 14/2, 2012 at 11:10 Comment(6)
That line couldn't be compiling!Defalcation
Is today a class member (field) or a local variable inside a method?Enunciate
local variable inside a methodThump
The modern version of the same question (with answer): Writing and testing convenience methods using Java 8 Date/Time classesLietuva
Update… FYI, the terrible Calendar class was years ago supplanted by the modern java.time classes defined in JSR 310.Asare
You can fake a clock with the static methods on the java.time.Clock class. Pass the faked Clock instance to the various methods on various java.time.* classes.Asare
M
19

As far as I see it you have three sensible options:

  1. Inject the Calendar instance in whatever method/class you set that day in.

    private void method(final Calendar cal) { Date today = cal.getTime(); }

  2. Use JodaTime instead of Calendar. This is less an option and more a case of a suggestion as JodaTime will make your life a lot easier. You will still need to inject this time in to the method.

    DateTime dt = new DateTime();

    Date jdkDate = dt.toDate();

  3. Wrap Calendar inside some interface that allows you to fetch the time. You then just mock that interface and get it to return a constant Date.

    Date today = calendarInterfaceInstance.getCurrentDate()

Meanwhile answered 14/2, 2012 at 11:23 Comment(3)
Joda Time's DateTimeUtils class has static methods that set the current time for all other Joda Time objects. This is very useful to set the time to a certain moment, for example for testing.Busby
@Busby - Yes that is true and a good point I neglected to mentionMeanwhile
Thank you guys. I followed ur suggestions and I moved to JodaTime. BTW it fixes easily the problem with something like: DateTimeUtils.setCurrentMillisFixed(new DateTime(2012, 2, 14, 13, 43, 21).getMillis());Thump
U
30

You can mock it using PowerMock in combination with Mockito:

On top of your class:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassThatCallsTheCalendar.class})

The key to success is that you have to put the class where you use Calendar in PrepareForTest instead of Calendar itself because it is a system class. (I personally had to search a lot before I found this)

Then the mocking itself:

mockStatic(Calendar.class);
when(Calendar.getInstance()).thenReturn(calendar);
Unblock answered 7/4, 2016 at 9:46 Comment(5)
which dependencies do you include to get mockStatic() method work?Cognition
Sorry I should have said that. I used import static to import the mockStatic method of PowerMockito. See this dependency for powermock mockito: mvnrepository.com/artifact/org.powermock/powermock-api-mockito2/…Unblock
You made my day!Wildee
+1 , but for the second part, both lines could be simplified to: PowerMockito.whenNew(Calendar.getInstance()).withAnyArguments().thenReturn(MY_INSTANCE_OBJECT_TO_FEED);Prudential
@Unblock Do you mind updating the answer to show where exactly those annotations and PowerMockito calls go inside your test class.Toddtoddie
M
19

As far as I see it you have three sensible options:

  1. Inject the Calendar instance in whatever method/class you set that day in.

    private void method(final Calendar cal) { Date today = cal.getTime(); }

  2. Use JodaTime instead of Calendar. This is less an option and more a case of a suggestion as JodaTime will make your life a lot easier. You will still need to inject this time in to the method.

    DateTime dt = new DateTime();

    Date jdkDate = dt.toDate();

  3. Wrap Calendar inside some interface that allows you to fetch the time. You then just mock that interface and get it to return a constant Date.

    Date today = calendarInterfaceInstance.getCurrentDate()

Meanwhile answered 14/2, 2012 at 11:23 Comment(3)
Joda Time's DateTimeUtils class has static methods that set the current time for all other Joda Time objects. This is very useful to set the time to a certain moment, for example for testing.Busby
@Busby - Yes that is true and a good point I neglected to mentionMeanwhile
Thank you guys. I followed ur suggestions and I moved to JodaTime. BTW it fixes easily the problem with something like: DateTimeUtils.setCurrentMillisFixed(new DateTime(2012, 2, 14, 13, 43, 21).getMillis());Thump
A
12

Don't mock it - instead introduce a method you can mock that gets dates. Something like this:

interface Utility {

    Date getDate();
}

Utilities implements Utility {


    public Date getDate() {

        return Calendar.getInstance().getTime();
    }

}

Then you can inject this into your class or just use a helper class with a bunch of static methods with a load method for the interface:

public class AppUtil {

    private static Utility util = new Utilities();

    public static void load(Utility newUtil) {

         this.util = newUtil;
    }

    public static Date getDate() {

        return util.getDate();
    }

}

Then in your application code:

private void doSomething() {
   Date today = AppUtil.getDate();
   ....
}

You can then just load a mock interface in your test methods.

@Test
public void shouldDoSomethingUseful() {
     Utility mockUtility = // .. create mock here
     AppUtil.load(mockUtility);

     // .. set up your expectations

     // exercise the functionality
     classUnderTest.doSomethingViaAPI();

     // ... maybe assert something 

}

See also Should you only mock types you own? and Test smell - everything is mocked

Anglian answered 14/2, 2012 at 11:21 Comment(0)
F
6

Using Mockito and PowerMockito:

Calendar endOfMarch = Calendar.getInstance();
endOfMarch.set(2011, Calendar.MARCH, 27);
PowerMockito.mockStatic(Calendar.class);
Mockito.when(Calendar.getInstance()).thenReturn(endOfMarch);

Refer to the link for the complete code.

Finisterre answered 14/4, 2016 at 15:14 Comment(0)
D
2

Write a class called DateHelper with a method getCalendar that returns Calendar.getInstance(). Refactor the class that you're testing so that it has a member variable of type DateHelper, and a constructor that injects that member variable. Use that constructor in your test, to inject a mock of DateHelper, in which getCalendar has been stubbed to return some known date.

Dryad answered 14/2, 2012 at 11:23 Comment(0)
B
2

With Mockk

 private lateinit var calendar: Calendar

    @Before
    fun setup() {
        calendar = mockk(relaxed = true)
        mockkStatic(Calendar::class)
        every { Calendar.getInstance() } returns calendar
    }

    @Test
    fun `test date when date is less than 16 month is 0`() {
        every { calendar[Calendar.DAY_OF_MONTH] } returns 12
        every { calendar[Calendar.MONTH] } returns 0
        every { calendar[Calendar.YEAR] } returns 2023

      // assert here

    }

refer to the link for mockk documentation

Bitterweed answered 5/6, 2023 at 10:47 Comment(2)
In 2023 neither you nor anyone else should want to use (and thereofre also not want to mock) the Calendar class. It was a mostly failed attempt to make up for the deficiencies of Date and was always cumbersome to work with. Both Date and Calendar were obsoleted with the release of java.time, the modern Java date and time API, nearly 10 years ago. Among many features java.time comes with the possibility of supplying a clock that you control to the methods that obtain the current time, so you don’t need mocking at all.Lietuva
@OleV.V. that's true :) but sometimes you have to deal with legacy codes and legacy frameworks: I have some thymeleaf templates that uses dates and thus the good old Calendar :'(Lessard
R
1

You can mockit using JMockit. Here you can see how you can do it: Mock Java Calendar - JMockit vs Mockito.

Racquelracquet answered 4/9, 2012 at 10:23 Comment(0)
E
1

You can do that as follows using Junit 5 and Mockito

@Test
public void doSomething() {
    listCalendarParams("now", Calendar.getInstance());
    Calendar fakedCalendar = new GregorianCalendar(2023, Calendar.OCTOBER, 15, 10, 15);
    listCalendarParams("mock", fakedCalendar);
    try (MockedStatic<Calendar> mockedStatic = Mockito.mockStatic(Calendar.class, Mockito.CALLS_REAL_METHODS)) {
        mockedStatic.when(Calendar::getInstance).thenReturn(fakedCalendar);
        //This or any method here using Calendar.getInstance() will be replaced by mock defined above
        Calendar calendar = Calendar.getInstance();
        listCalendarParams("evaluated", calendar);
        assertEquals(Calendar.OCTOBER, calendar.get(Calendar.MONTH));
        assertEquals(15, calendar.get(Calendar.DAY_OF_MONTH));
        assertEquals(10, calendar.get(Calendar.HOUR_OF_DAY));
        assertEquals(15, calendar.get(Calendar.MINUTE));
    }
}

private static void listCalendarParams(String nameOfCal, Calendar calendar) {
    System.out.println(nameOfCal + ":\t" +
            new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss")
                    .format(calendar.getTime()));
}3

Output:

now:        2023-11-27T14:00:48 //This will change anytime you run it.
mock:       2023-10-15T10:15:00
evaluated:  2023-10-15T10:15:00
Elutriate answered 27/11, 2023 at 13:3 Comment(0)
B
0

Avoid use Calendar.getInstance() and just use Mockito methods to return what you like. For example:

@Test
fun italianLocale_returnsItalianFormatDate() {
    val calendar: Calendar = Mockito.mock(Calendar::class.java)
    Mockito.`when`(calendar.get(Calendar.DAY_OF_MONTH)).thenReturn(27)
    Mockito.`when`(calendar.get(Calendar.YEAR)).thenReturn(2023)
    Mockito.`when`(calendar.get(Calendar.MONTH)).thenReturn(1)
    val formatted = calendar.toReadableDate()
    assert(formatted == "27/01/2023")
}

import Mockito in your gradle file with:

testImplementation ("org.mockito.kotlin:mockito-kotlin:x.x.x")

or (if you are using groovy)

testImplementation "org.mockito.kotlin:mockito-kotlin:x.x.x"
Ba answered 27/1, 2023 at 9:36 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.