How to test passing of time in jUnit test without accessing private variables?

java, junit, tdd, unit-testing

Solution

- The test could do with some refactoring to make the intent clearer. If what I comprehend is correct...

.

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));
}

- Now to the matter of testing without having to expose private state

- How is the private variable modified in real life ? Is there some other public method you could call which updates the access time?

- Since the clock/time is an important concept, why not make that explicit as a role. So you could pass a Clock object to the Session, which it uses to update its internal access time. In your tests, you could pass in a MockClock, whose getCurrentTime() method would return whatever value you wish. I'm making up the mocking syntax.. so update with whatever you are using.

.

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));
}

Problem

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.

Original source

Related problems