34

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?

Randomize
  • 8,651
  • 18
  • 78
  • 133

8 Answers8

29

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);
GoGoris
  • 820
  • 8
  • 20
  • 1
    which dependencies do you include to get mockStatic() method work? – Leo DroidCoder Mar 13 '17 at 15:18
  • Sorry I should have said that. I used import static to import the mockStatic method of PowerMockito. See this dependency for powermock mockito: http://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2/1.6.6 – GoGoris Mar 14 '17 at 19:01
  • You made my day! – Anand Vaidya Sep 09 '18 at 14:55
  • +1 , but for the second part, both lines could be simplified to: `PowerMockito.whenNew(Calendar.getInstance()).withAnyArguments().thenReturn(MY_INSTANCE_OBJECT_TO_FEED);` – Payam Jun 25 '21 at 00:48
  • @GoGoris Do you mind updating the answer to show where exactly those annotations and PowerMockito calls go inside your test class. – lasec0203 Nov 03 '22 at 03:55
18

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

BeRecursive
  • 6,286
  • 1
  • 24
  • 41
  • 4
    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. – Jesper Feb 14 '12 at 12:16
  • @Jesper - Yes that is true and a good point I neglected to mention – BeRecursive Feb 14 '12 at 12:26
  • 1
    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()); – Randomize Feb 14 '12 at 14:48
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

Community
  • 1
  • 1
blank
  • 17,852
  • 20
  • 105
  • 159
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.

Surekha
  • 582
  • 1
  • 7
  • 12
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.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
1

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

Behrang
  • 46,888
  • 25
  • 118
  • 160
1

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

inder
  • 303
  • 2
  • 12
  • 1
    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,](https://docs.oracle.com/javase/tutorial/datetime/index.html) 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. – Ole V.V. Jun 05 '23 at 11:04
  • 1
    @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 :'( – Sylvain Jun 15 '23 at 12:19
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"
Nicola Gallazzi
  • 7,897
  • 6
  • 45
  • 64