Reputation: 5398
I ma trying to write a Junit for the following code;
/**
* Check if a date is greater than 24 hours
* @param dateToCheck the date to check
* @return true if it is greater than otherwise false
*/
public static boolean dateGreaterThan24Hours(Date dateToCheck){
if(dateToCheck == null){
throw new IllegalArgumentException("The date passed to check for greater than 24 hours is null");
}
long millisIn24Hours = 1000 * 60 * 60 * 24;
Date hours24ago = new Date(new Date().getTime() - millisIn24Hours);
if (dateToCheck.before(hours24ago)) {
//24 hrs have passed
return true;
}
return false;
}
However I am struggling to do so because the method only accepts a date to check against. Meaning y current attempt at a test method is clearly going to fail;
@Test
public void checkLessThan24HoursShouldReturnTrue(){
//Calendar represents the 7th of July 2014 at 17.30pm
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2014);
cal.set(Calendar.MONTH, 07);
cal.set(Calendar.DAY_OF_MONTH, 7);
cal.set(Calendar.HOUR_OF_DAY,17);
cal.set(Calendar.MINUTE,30);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
Date july7 = cal.getTime();
//Calendar represents the 6th of July 2014 at 18.30pm
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2014);
cal.set(Calendar.MONTH, 07);
cal.set(Calendar.DAY_OF_MONTH, 6);
cal.set(Calendar.HOUR_OF_DAY,18);
cal.set(Calendar.MINUTE,30);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
Date july6 = cal.getTime();
}
Can anyone suggest how i can refactor the original method to make it easier to test?
Thanks
Upvotes: 5
Views: 6399
Reputation: 17595
If you could refactor you code to be test friendly:
static final long millisIn24Hours = 1000 * 60 * 60 * 24;
public static boolean dateGreaterThan24Hours(Date dateToCheck){
return dateGreaterThan(dateToCheck,
Calendar.getInstance().getTime(), - millisIn24Hours);
}
public static boolean dateGreaterThan(Date date, Date than, long msDiff) {
if(date == null){
throw new IllegalArgumentException("date is null");
}
if(than == null){
throw new IllegalArgumentException("than is null");
}
return date.before(new Date(than.getTime() + msDiff));
}
You could have somethign like this:
public void testDateGreaterTrue() {
Date now = new Date();
assertFalse(dateGreaterThan(now,
new Date(now.getTime()-millisIn24Hours), millisIn24Hours));
}
public void testDateGreaterFalse() {
Date now = new Date();
assertFalse(dateGreaterThan(now,
new Date(now.getTime()-millisIn24Hours-1), millisIn24Hours));
}
Instead of:
public void testDateGreaterThan24HoursTrue() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) - 25);
assertTrue(dateGreaterThan24Hours(cal.getTime()));
}
public void testDateGreaterThan24HoursFalse() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) - 23);
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 59);
cal.set(Calendar.SECOND, cal.get(Calendar.SECOND) + 59);
assertFalse(dateGreaterThan24Hours(cal.getTime()));
}
Upvotes: 0
Reputation: 31648
If you can use the new Java 8 Classes (or JodaTime) you can write much cleaner code.
Plus, depending on why you want 24 hrs ago, there might be bugs regarding Daylight savings time since you only do 24 hrs as milliseconds which might not be a full day ago on certain days.
Here is the equivalent code using the new Java 8 time library. I renamed the method to be more intent revealing and added a Clock
optional parameter that allows you to set the time for tests:
public static boolean checkDateMorethan24HrsOld(Date dateToCheck){
return checkDateMorethan24HrsOld(dateToCheck, Clock.systemDefaultZone());
}
public static boolean checkDateMorethan24HrsOld(Date dateToCheck, Clock clock){
if(dateToCheck == null){
//maybe throw NullPointerInstead?
throw new IllegalArgumentException("The date is null");
}
Objects.requireNonNull(clock);
//use clock.instant().minus(Period.ofDays(1)) to support DST
Instant time24HrsAgo =clock.instant().minus(Duration.ofHours(24));
return dateToCheck.toInstant().compareTo(time24HrsAgo) <0;
}
Then your test:
@Test
public void checkLessThan24HoursShouldReturnTrue(){
Instant clocktime = Instant.parse("2014-07-07T17:30:00Z");
Clock clock =Clock.fixed(clocktime, ZoneId.systemDefault());
assertTrue(checkDateMorethan24HrsOld(Date.from(clocktime.minus(Duration.ofDays(2))), clock));
assertFalse("exactly 24 hrs ago",
checkDateMorethan24HrsOld(Date.from(clocktime.minus(Duration.ofDays(1))), clock));
assertFalse(checkDateMorethan24HrsOld(Date.from(clocktime.minus(Duration.ofHours(1))), clock));
}
Of course this code is even simplier if the method takes an Instant
instead of Date
.
Upvotes: 0
Reputation: 46219
You don't have to change the original method. The easiest tests to write would be:
@Test
public void checkMoreThan24HoursShouldReturnTrue() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, -25);
assertTrue(YourClass.dateGreaterThan24Hours(cal.getTime()));
}
@Test
public void checkLessThan24HoursShouldReturnFalse() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, -23);
assertFalse(YourClass.dateGreaterThan24Hours(cal.getTime()));
}
Also I would recommend some refactorings as suggested by @DaveNewton in the comments, and a test that verifies your custom exception for null
argument.
Upvotes: 5