Unit testing with PowerMock, precision problem when comparing seconds - java

I'm trying to unit test a time function using powermock, but I'm sometimes having trouble getting through the assertEquals() when comparing the current time with system clock. The expected time is 2 seconds off. Here is my code for reference:
#RunWith(PowerMock Runner.class)
#PrepareForTest({Calendar.class, time.class})
public class TEST {
#Test
public void testTime() {
PowerMockito.mockStatic(Calendar.class);
Mockito.when(Calendar.getInstance()).thenReturn(calendarInstance);
SimpleDateFormat sdf = new SimpleDateFormat(DD_DOT_MM_DOT_YYY);
assertEquals(sdf.format(calendarInstance.getTime()), time.getDate(DD_DOT_MM_DOT_YYYY)); }
}
//inside the time.getDate function
public static String getDate(String format) {
DateFormat dateFormat = new SimpleDateFormat(format);
Date date = new Date();
return dateFormat.format(date);
}
And here's the Junit output I sometimes get which indicates the failure:
org.junit.ComparisonFailure:
Expected :20:43:18
Actual :20:43:20
I have tried a few ways to get around this, but I couldn't get it working reliably. Thank you for your help in advance.

The code sample does not show how time object in time.getDate being initialised.
For this test - you should initialise both the time and Calendar.getInstance() with same seed value else the result is unpredictable.

Related

Java Unit Testing method that uses new Date() for current date [duplicate]

This question already has answers here:
Unit testing time-based logic in Java
(3 answers)
Handling unit tests with a condition on the current time
(6 answers)
Setting time and date in JUnit test fixture
(4 answers)
Writing and testing convenience methods using Java 8 Date/Time classes
(1 answer)
Closed 3 years ago.
So I have a class that has a method "getDaysUntil(Date date)" which returns the number of days until the date given as parameter. I mention that I cannot change the class below:
public class A {
public int getDaysUntil(Date givenDate) {
... // code
Date currentDate = new Date() //it creates a date object holding the current day
...// code that calculates the nr of days between currentDate and givenDate.
}
I have to do some unit testing and you might see the problem, it creates currentDate inside the method and the returned value will be different from day to day. I have tried to mock a Date object or "override" System.currentTimeMillis() with PowerMock but to no avail.
Is there any way to properly test these kind of methods?
Use a class that serves as a DateFactory, which is called to construct Date objects in your application code.
Then just mock the method of that DateFactory in your unit test. That way you can make it return whatever date you want as a virtual "current date"
One solution where System.currentTimeMillis() is mocked is as follows, using the JMockit library (it should be possible with PowerMock too):
#Test #SuppressWarnings("deprecation")
public void daysUntilCurrentDate() {
final long fakeCurrentDateInMillis = new Date(2017, 2, 1).getTime();
new MockUp<System>() {
#Mock long currentTimeMillis() { return fakeCurrentDateInMillis; }
};
A tested = new A();
int daysSinceJan30 = tested.getDaysUntil(new Date(2017, 1, 30));
assertEquals(2, daysSinceJan3O);
}
I understand that you cannot change the method that you need to test. Unfortunately this also means that you are stuck with the old and often not very programmer-friendly Date class (I am assuming java.util.Date).
Edit: The no-arg Date constructor that your method uses in turn uses System.currentTimeMillis(), a static native method. I didn’t know there were tools that could mock contructors and static native methods, but was informed by comment and answer by #Rogério, the developer of JMockit, that such mocking tools exist.
In any case, there is an alternative: you calculate some number of days from today, pass the resulting Date to the method and check that you get the number back you used in your calculation. This will work on any day and requires no mocking/stubbing.
In the code below I am assuming that the getDaysUntil method should discard the hours and minutes and just look at the date in the computer’s time zone. If the real requirements differ, you can probably make the appropriate adjustments to my code.
We want to take into account that the method may run over midnight. If so, I consider the result undefined since we do not know whether the Date object was constructed before or after midnight. In this case I simply try again, assuming the test will finish before the next midnight.
#Test
public void testGetDaysUntil() {
A instanceUnderTest = new A();
for (int daysToTest = 0; daysToTest <= 400; daysToTest++) {
LocalDate today;
int result;
do {
today = LocalDate.now(); // do this in each iteration in case day changes underway
LocalDate targetDate = today.plusDays(daysToTest);
Date midnightAtStartOfDay = Date.from(targetDate.atStartOfDay(ZoneId.systemDefault())
.toInstant());
result = instanceUnderTest.getDaysUntil(midnightAtStartOfDay);
} while (! today.equals(LocalDate.now())); // if we have passed midnight, try again
assertEquals(daysToTest, result);
do {
today = LocalDate.now();
LocalDate targetDate = today.plusDays(daysToTest);
Date nearMidnightAtEndOfDay = Date.from(targetDate.atTime(23, 59, 59, 400_000_000)
.atZone(ZoneId.systemDefault())
.toInstant());
result = instanceUnderTest.getDaysUntil(nearMidnightAtEndOfDay);
} while (! today.equals(LocalDate.now()));
assertEquals(daysToTest, result);
}
}
I have used the Java 8 classes for the date and time calculations. If you cannot use Java 8, Calendar and/or GregorianCalendar can be used, they may be just a little more cumbersome for this job, but at least can be converted to Date easily.

JUnit with Java 8 Clock

Imagine we have the following class that I would like to test:
class Times {
Clock cl = Clock.systemDefaultZone();
public int changeTime() {
LocalDate ld = LocalDate.now(cl);
if(ld.getMonth().equals(Month.OCTOBER))
return 1;
if(ld.getMonth().equals(Month.NOVEMBER))
return 2;
return 0;
}
}
How can I force the date to be in November and assert that the method returns 2?
I am using JUnit and Mockito.
Assuming there is a setter for the Clock object in Times class, you can do something like this:
Times times = new Times();
Clock fixedClockInNovember = Clock.fixed(Instant.parse("2015-11-01T00:00:00.00Z"), ZoneId.of("UTC"));
times.setClock(fixedClockInNovember);
assertEquals(2, times.changeTime());
In this code, a fixed clock is created. This simulates a constant time for the given instant, that is in November.
As such, any call to LocalDate.now(clock) will always return the same date in November.

Java system time

I have this code copied from one of questions from SO:
public static String getCurrentTimeStamp() {
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
String strDate = sdfDate.format(now);
return strDate;
}
I want to get only the system time and NOT the date. Then I must change second line of code to:
SimpleDateFormat sdfDate = new SimpleDateFormat(" HH:mm:ss") ;
Then, DATE() must get the current time. Clear upto this point but I can't understand the format() function used.
I mean cant we simply output variable now instead of strdate?
Is it just because that the return type of function getCurrentTimeStamp() is String?
Please clarify and if there is any other simpler and one line code for getting system time alone, do share.
I mean cant we simply output variable now instead of strdate.
Well you could return now.toString() - but that will use the format that Date.toString() happens to choose, whereas you want a specific format. The point of the SimpleDateFormat object in this case is to convert a Date (which is a point in time, without reference to any particular calendar or time zone) into a String, applying an appropriate time zone, calendar system, and text format (in your case HH:mm:ss).
You can still simplify your method somewhat though, by removing the local variables (which are each only used once):
public static String getCurrentTimeStamp() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
Or maybe you'd find it more readable to keep the variable for the date format, but not the date and the return value:
public static String getCurrentTimeStamp() {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
return format.format(new Date());
}
Personally I'd recommend using Joda Time instead, mind you - it's a much nicer date/time API, and its formatted are thread-safe so you could easily keep a reference to a single formatting object.
public static String getCurrentTimeStampwithTimeOnly() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
Helps you to do this.
you can call this line any time
Date now = new Date();
The now variable will contain the current timestamp
The format function just generates a String from this timestamp
also take a look at the Calendar class ( Calendar.getInstance())

Java Date instances issue

I got the following in the body of the code:
public String getStartDate() {
return (new SimpleDateFormat("dd/MM/yyyy").format(startDate));
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
and startDate is initiated as Date startDate;
When I use the set startdate method, I am setting the value of this instance to the value of the object retrieved from database..
setStartDate(rsvIns.startDate);
Now the problem is here when I try to see if the startDate is equal to TodayDate,
Date todayDate = new Date();
if(startDate.equals(todayDate))
{
changeStatus(requestID, "Active");
}else{
//update status if others approved
changeStatus(requestID, "Approved");
}
I whenever it comes to the equals line it throws error and just doesn't execute the if or else part, hence the statement is never executed. Any idea why this could be happening?
Thanks,
I see 2 problems from the code snippets you provided:
Date todayDate = new Date() object contains in information on Hour, Minutes, Seconds and Milliseconds. If you compare it to a date created from a "dd/MM/yyyy" string you will have a problem caused by the time component of the Date object.
getStartDate() returns a String object. If that value is assigned to the startDate variable from the if statement, then on equals() call with a Date object parameter value the result will be false regardless of the startDate and parameter value, because the classes do not match.
If you get into situations like this and you do not see the answer, copy the code into a small program and debug trace it, look at the values of the objects. Trace into java code.
I have seen many times java developers with years of experience trip on the time component of Date object. They even clear the Hour, Minutes and Seconds and forget about the Milliseconds component.

How to initialize Mock with variable data

I have this method to test :
public static Date getDateSinceUTC(CstOrderBean orderBean) {
int year = orderBean.getDeadLineYear();
int month = orderBean.getDeadLineMonth();
int day = orderBean.getDeadLineDay();
int hour = orderBean.getDeadLineHour();
int minute = orderBean.getDeadLineMinute();
String ap = orderBean.getDeadLineAmPm() == 1 ? "PM" : "AM";
//TODO AM=0, PM=1 comes from html form
SimpleDateFormat df = new SimpleDateFormat("yyyy:MM:dd:hh:mm:aa");
String stringDate = stringifyIntegers(":", year, month, day, hour, minute);
stringDate = stringDate.concat(ap);
Date date;
try {
date = df.parse(stringDate);
} catch (ParseException e) {
throw new Error("Parsing date from html form failed", e);
}
return date;
}
Where CstOrderBean needs to be mocked by Mockito because it is not a POJO (some static initializations etc. - from source code generator). But I need to run the method xxx times, hence set the mocks with many data combinations
I could use TestNG's #DataProvider to do that. But I'm not sure how to do that, I guess that :
when(ob.getDeadLineYear()).thenReturn(1, 2, 3);
....
in loop is a bad idea, isn't it ? Is the correct way of doing this to create xx mocks and initialize them like that ?
Each test should get their own mock that preferably does not have variable data. If you use several different return values from the same mock object then the testing has to be white-box testing as the test is coupled with the number of calls to a mocked method instead of the result of the method under test.
That said, you are able to define a set of return values with calling thenReturn repeatedly or by defining the return values as varargs
when(ob.getDeadLineYear()).thenReturn(someValue, anotherValue, ..., ultimateValue);
This might be cleaner as you should probably control the values that the mock returns anyway.
How you mock depends on what you would like to test. Looping on the deadline year might not do the job you want it to.
One test for seeing if a leap year works might be something like:
when(ob.getDeadLineYear()).thenReturn(2000);
when(ob.getDeadLineMonth()).thenReturn(2);
when(ob.getDeadLineDay()).thenReturn(29);
when(ob.getDeadLineHour()).thenReturn(12);
when(ob.getDeadLineMinute()).thenReturn(0);
when(ob.getDeadDeadLineAmPm()).thenReturn(1);
assertTrue("Got unexpected date", getDateSinceUTC(ob).toString().startsWith("2000-02-29 12:00:00"));
(Warning: above codes was typed in by hand). Mix, match, and repeat for other dates that you need to test to verify that getDateSinceUTC is working. You might want a separate test method to check invalid dates, like 2/30/2012 (and expect a throw). You might want to check invalid times like 23:61. You might want to check valid dates, like your birthdate.
Instead of a loop on the year, please look at "normal" cases, borderline cases, and error cases. This is the better practice for unit testing.

Categories