I'm unit testing a class where I need a certain amount of time to pass before I can check results. Specifically I need x minutes to pass before I can tell whether the test worked or not. I have read that in unit testing we should be testing the interface and not the implementation, so we should not be accessing private variables, but other than putting a sleep in my unit test I don't know how to test without modifying private variables.
My test is set up like this:
@Test
public void testClearSession() {
final int timeout = 1;
final String sessionId = "test";
sessionMgr.setTimeout(timeout);
try {
sessionMgr.createSession(sessionId);
} catch (Exception e) {
e.printStackTrace();
}
DBSession session = sessionMgr.getSession(sessionId);
sessionMgr.clearSessions();
assertNotNull(sessionMgr.getSession(sessionId));
Calendar accessTime = Calendar.getInstance();
accessTime.add(Calendar.MINUTE, - timeout - 1);
session.setAccessTime(accessTime.getTime()); // MODIFY PRIVATE VARIABLE VIA PROTECTED SETTER
sessionMgr.clearSessions();
assertNull(sessionMgr.getSession(sessionId));
}
Is it possible to test this other than modifying the accessTime private variable (via creating the setAccessTime setter or reflection), or inserting a sleep in the unit test?
EDIT 11-April-2012
I am specifically trying to test that my SessionManager object clears sessions after a specific period of time has passed. The database I am connecting to will drop connections after a fixed period of time. When I get close to that timeout, the SessionManager object will clear the sessions by calling a "finalise session" procedure on the database, and removing the sessions from it's internal list.
The SessionManager object is designed to be run in a separate thread. The code I am testing looks like this:
public synchronized void clearSessions() {
log.debug("clearSessions()");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, - timeout);
Iterator<Entry<String, DBSession>> entries = sessionList.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, DBSession> entry = entries.next();
DBSession session = entry.getValue();
if (session.getAccessTime().before(cal.getTime())) {
// close connection
try {
connMgr.closeconn(session.getConnection(), entry.getKey());
} catch (Exception e) {
e.printStackTrace();
}
entries.remove();
}
}
}
The call to connMgr (ConnectionManager object) is a bit convoluted, but I am in the process of refactoring legacy code and it is what it is at the moment. The Session object stores a connection to the database as well as some associated data.
The best way to test a private method is via another public method. If this cannot be done, then one of the following conditions is true: The private method is dead code. There is a design smell near the class that you are testing.
If a JUnit test method is declared as "private", it compiles successfully. But the execution will fail. This is because JUnit requires that all test methods must be declared as "public".
.
public void TestClearSessionsMaintainsSessionsUnlessLastAccessTimeIsOverThreshold() {
final int timeout = 1;
final String sessionId = "test";
sessionMgr = GetSessionManagerWithTimeout(timeout);
DBSession session = CreateSession(sessionMgr, sessionId);
sessionMgr.clearSessions();
assertNotNull(sessionMgr.getSession(sessionId));
session.setAccessTime(PastInstantThatIsOverThreshold()); // MODIFY PRIVATE VARIABLE VIA PROTECTED SETTER
sessionMgr.clearSessions();
assertNull(sessionMgr.getSession(sessionId));
}
.
public void TestClearSessionsMaintainsSessionsUnlessLastAccessTimeIsOverThreshold() {
final int timeout = 1;
final String sessionId = "test";
expect(mockClock).GetCurrentTime(); willReturn(CurrentTime());
sessionMgr = GetSessionManagerWithTimeout(timeout, mockClock);
DBSession session = CreateSession(sessionMgr, sessionId);
sessionMgr.clearSessions();
assertNotNull(sessionMgr.getSession(sessionId));
expect(mockClock).GetCurrentTime(); willReturn(PastInstantThatIsOverThreshold());
session.DoSomethingThatUpdatesAccessTime();
sessionMgr.clearSessions();
assertNull(sessionMgr.getSession(sessionId));
}
It looks like functionality being tested is SessionManager evitcs all expired sessions.
I would consider creating test class extending DBSession.
AlwaysExpiredDBSession extends DBSession {
....
// access time to be somewhere older 'NOW'
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With