String parsed date test in JUnit? - java

I'm trying to verify a method that contains date parsing implementation.
#Override
public void pasreDates(String startDate, String endDate) {
LocalDateTime startDateTime = null;
LocalDateTime endDateTime = null;
// parsing date and times
try {
startDateTime = LocalDateTime.parse(startDate, DATE_TIME_FORMATTER);
endDateTime = LocalDateTime.parse(endDate, DATE_TIME_FORMATTER);
} catch (DateTimeParseException e) {
LOGGER.error(ERROR_PARSING_DATE_TIME, e);
}
}
I just tried to test this using JUnit the date and times(both dates) and verify the log if an exception occurred. My test method looks like
#Test
public void should_log_an_error_if_given_date_string_is_in_invalid_format() {
dealMapsUtil.storeDataToProductIdToDealDateMap(dummyProductId, dummyDeal, dummyStartDate, dummyEndDate);
verify(logger, times(1)).error(ERROR_PARSING_DATE_TIME);
}
How I verify the date format and what are the other test cases that I can write for this method? I've went through few tutorials but dind't help.

As #Timothy Truckle correctly noticed, you don't verify what you invoke.
You invoke logger.error(String, Throwable) but you verify logger.error(String). It can only fail.
Whatever, mocking and verifying Logger is generally not straight and it should be performed only as we don't have choice.
1) Actually you don't unit test parseDates(). You test it in "integration" as you invoke a method that invokes it :
dealMapsUtil.storeDataToProductIdToDealDateMap
Unit tests should focus on the tested behavior.
If this method can be invoked from multiple places, you should wonder if it would not be relevant to test it first in its own class and to "verify" that it is called in the tests of the caller classes.
2) parseDates() doesn't define a clear API for client classes as the single way to check if the parse failed are logs.
Logs and behavior results are distinct things and the first one should not replace the second one.
To make it more handly, you could return a boolean to indicate the result of the parsing or throw an exception in parseDates().
In this way you could test this method very straightly.

Related

Unit testing with PowerMock, precision problem when comparing seconds

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.

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.

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.

Java Date exception handling try catch

Is there some sort of exception in Java to catch an invalid Date object? I'm trying to use it in the following method, but I don't know what type of exception to look for. Is it a ParseException.
public boolean setDate(Date date) {
this.date = date;
return true;
}
In the method you provide, there is no way to catch an exception, because none will be thrown by the simple assignment. All you can do is maybe the below change:
if(date == null) return false;
But even that's not graceful. You may want to do something with this.date or throw an exception up if that's the desired behavior.
What you are really seeking is either:
ParseException - thrown by a DateFormat object when it attempts to parse(), which would happen before your set method
IllegalArgumentException - thrown by a SimpleDateFormat constructor, again it would happen before your set method. Indicates you provided an invalid format string.
You'd want to catch one of those (probably #1). But it has to happen before your method call. Once you have a Date object, it is either null or valid.
This might not be related to the original question. But you must notice the method name, which is setDate(). Do you think it sounds like it will return something? Or if it may, then do you think its a good idea to return a boolean there? IMO, do something like this,
public void setDate(Date date) {
this.date = date;
}
public boolean isDateNull() { // or something
return this.date == null;
}
In this method, there is no need to worry about an exception. The date is already created by the time you get into this method. If you are parsing a date, it would have done outside of this code. The best you could do is make sure the date is not null.
It depends on what you mean by an invalid date. Did you mean to give us a method signature that looked more like this?
public void setDate(String date) throws ParseException {
this.date = SomeDateFormat.getInstance().format(date);
}
Otherwise, as the others stated the simple act of assigning a Java date object to a field shouldn't be exceptional as it is either an instance of Date already, or null.
If you are just trying to parse a string into a java.util.Date, look at DateFormat, FastDateFormat (apache, thread safe), or Joda Time.

Determine if a String is a valid date before parsing

I have this situation where I am reading about 130K records containing dates stored as String fields. Some records contain blanks (nulls), some contain strings like this: 'dd-MMM-yy' and some contain this 'dd/MM/yyyy'.
I have written a method like this:
public Date parsedate(String date){
if(date !== null){
try{
1. create a SimpleDateFormat object using 'dd-MMM-yy' as the pattern
2. parse the date
3. return the parsed date
}catch(ParseException e){
try{
1. create a SimpleDateFormat object using 'dd/MM/yyy' as the pattern
2. parse the date
3. return parsed date
}catch(ParseException e){
return null
}
}
}else{
return null
}
}
So you may have already spotted the problem. I am using the try .. catch as part of my logic. It would be better is I can determine before hand that the String actually contains a parseable date in some format then attempt to parse it.
So, is there some API or library that can help with this? I do not mind writing several different Parse classes to handle the different formats and then creating a factory to select the correct6 one, but, how do I determine which one?
Thanks.
See Lazy Error Handling in Java for an overview of how to eliminate try/catch blocks using an Option type.
Functional Java is your friend.
In essence, what you want to do is to wrap the date parsing in a function that doesn't throw anything, but indicates in its return type whether parsing was successful or not. For example:
import fj.F; import fj.F2;
import fj.data.Option;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import static fj.Function.curry;
import static fj.Option.some;
import static fj.Option.none;
...
F<String, F<String, Option<Date>>> parseDate =
curry(new F2<String, String, Option<Date>>() {
public Option<Date> f(String pattern, String s) {
try {
return some(new SimpleDateFormat(pattern).parse(s));
}
catch (ParseException e) {
return none();
}
}
});
OK, now you've a reusable date parser that doesn't throw anything, but indicates failure by returning a value of type Option.None. Here's how you use it:
import fj.data.List;
import static fj.data.Stream.stream;
import static fj.data.Option.isSome_;
....
public Option<Date> parseWithPatterns(String s, Stream<String> patterns) {
return stream(s).apply(patterns.map(parseDate)).find(isSome_());
}
That will give you the date parsed with the first pattern that matches, or a value of type Option.None, which is type-safe whereas null isn't.
If you're wondering what Stream is... it's a lazy list. This ensures that you ignore patterns after the first successful one. No need to do too much work.
Call your function like this:
for (Date d: parseWithPatterns(someString, stream("dd/MM/yyyy", "dd-MM-yyyy")) {
// Do something with the date here.
}
Or...
Option<Date> d = parseWithPatterns(someString,
stream("dd/MM/yyyy", "dd-MM-yyyy"));
if (d.isNone()) {
// Handle the case where neither pattern matches.
}
else {
// Do something with d.some()
}
Don't be too hard on yourself about using try-catch in logic: this is one of those situations where Java forces you to so there's not a lot you can do about it.
But in this case you could instead use DateFormat.parse(String, ParsePosition).
You can take advantage of regular expressions to determine which format the string is in, and whether it matches any valid format. Something like this (not tested):
(Oops, I wrote this in C# before checking to see what language you were using.)
Regex test = new Regex(#"^(?:(?<formatA>\d{2}-[a-zA-Z]{3}-\d{2})|(?<formatB>\d{2}/\d{2}/\d{3}))$", RegexOption.Compiled);
Match match = test.Match(yourString);
if (match.Success)
{
if (!string.IsNullOrEmpty(match.Groups["formatA"]))
{
// Use format A.
}
else if (!string.IsNullOrEmpty(match.Groups["formatB"]))
{
// Use format B.
}
...
}
If you formats are exact (June 7th 1999 would be either 07-Jun-99 or 07/06/1999: you are sure that you have leading zeros), then you could just check for the length of the string before trying to parse.
Be careful with the short month name in the first version, because Jun may not be June in another language.
But if your data is coming from one database, then I would just convert all dates to the common format (it is one-off, but then you control the data and its format).
In this limited situation, the best (and fastest method) is certinally to parse out the day, then based on the next char either '/' or '-' try to parse out the rest. and if at any point there is unexpected data, return NULL then.
Assuming the patterns you gave are the only likely choices, I would look at the String passed in to see which format to apply.
public Date parseDate(final String date) {
if (date == null) {
return null;
}
SimpleDateFormat format = (date.charAt(2) == '/') ? new SimpleDateFormat("dd/MMM/yyyy")
: new SimpleDateFormat("dd-MMM-yy");
try {
return format.parse(date);
} catch (ParseException e) {
// Log a complaint and include date in the complaint
}
return null;
}
As others have mentioned, if you can guarantee that you will never access the DateFormats in a multi-threaded manner, you can make class-level or static instances.
Looks like three options if you only have two, known formats:
check for the presence of - or / first and start with that parsing for that format.
check the length since "dd-MMM-yy" and "dd/MM/yyyy" are different
use precompiled regular expressions
The latter seems unnecessary.
Use regular expressions to parse your string. Make sure that you keep both regex's pre-compiled (not create new on every method call, but store them as constants), and compare if it actually is faster then the try-catch you use.
I still find it strange that your method returns null if both versions fail rather then throwing an exception.
you could use split to determine which format to use
String[] parts = date.split("-");
df = (parts.length==3 ? format1 : format2);
That assumes they are all in one or the other format, you could improve the checking if need be
An alternative to creating a SimpleDateFormat (or two) per iteration would be to lazily populate a ThreadLocal container for these formats. This will solve both Thread safety concerns and concerns around object creation performance.
A simple utility class I have written for my project. Hope this helps someone.
Usage examples:
DateUtils.multiParse("1-12-12");
DateUtils.multiParse("2-24-2012");
DateUtils.multiParse("3/5/2012");
DateUtils.multiParse("2/16/12");
public class DateUtils {
private static List<SimpleDateFormat> dateFormats = new ArrayList<SimpleDateFormat>();
private Utils() {
dateFormats.add(new SimpleDateFormat("MM/dd/yy")); // must precede yyyy
dateFormats.add(new SimpleDateFormat("MM/dd/yyyy"));
dateFormats.add(new SimpleDateFormat("MM-dd-yy"));
dateFormats.add(new SimpleDateFormat("MM-dd-yyyy"));
}
private static Date tryToParse(String input, SimpleDateFormat format) {
Date date = null;
try {
date = format.parse(input);
} catch (ParseException e) {
}
return date;
}
public static Date multiParse(String input) {
Date date = null;
for (SimpleDateFormat format : dateFormats) {
date = tryToParse(input, format);
if (date != null) break;
}
return date;
}
}
On one hand I see nothing wrong with your use of try/catch for the purpose, it’s the option I would use. On the other hand there are alternatives:
Take a taste from the string before deciding how to parse it.
Use optional parts of the format pattern string.
For my demonstrations I am using java.time, the modern Java date and time API, because the Date class used in the question was always poorly designed and is now long outdated. For a date without time of day we need a java.time.LocalDate.
try-catch
Using try-catch with java.time looks like this:
DateTimeFormatter ddmmmuuFormatter = DateTimeFormatter.ofPattern("dd-MMM-uu", Locale.ENGLISH);
DateTimeFormatter ddmmuuuuFormatter = DateTimeFormatter.ofPattern("dd/MM/uuuu");
String dateString = "07-Jun-09";
LocalDate result;
try {
result = LocalDate.parse(dateString, ddmmmuuFormatter);
} catch (DateTimeParseException dtpe) {
result = LocalDate.parse(dateString, ddmmuuuuFormatter);
}
System.out.println("Date: " + result);
Output is:
Date: 2009-06-07
Suppose instead we defined the string as:
String dateString = "07/06/2009";
Then output is still the same.
Take a taste
If you prefer to avoid the try-catch construct, it’s easy to make a simple check to decide which of the formats your string conforms to. For example:
if (dateString.contains("-")) {
result = LocalDate.parse(dateString, ddmmmuuFormatter);
} else {
result = LocalDate.parse(dateString, ddmmuuuuFormatter);
}
The result is the same as before.
Use optional parts in the format pattern string
This is the option I like the least, but it’s short and presented for some measure of completeness.
DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("[dd-MMM-uu][dd/MM/uuuu]", Locale.ENGLISH);
LocalDate result = LocalDate.parse(dateString, dateFormatter);
The square brackets denote optional parts of the format. So Java first tries to parse using dd-MMM-uu. No matter if successful or not it then tries to parse the remainder of the string using dd/MM/uuuu. Given your two formats one of the attempts will succeed, and you have parsed the date. The result is still the same as above.
Link
Oracle tutorial: Date Time explaining how to use java.time.

Categories