Untestable

It’s easy to write untestable code. It’s natural. Most code that we write will be hard to test unless we explicitly think about testing when we write it…

This code is really simple, yet it’s untestable. Why?


DateChangeManager.h

class CDateChangeManager  
{
   public :
      class Listener
      {
         public :
 
            virtual void OnDateChange(
               const CTime &oldDate,
               const CTime &newDate) = 0;
   
         protected :

            ~Listener() {}
      };

      CDateChangeManager();

      typedef unsigned long Cookie;

      Cookie RegisterForNotification(
         Listener &listener);

      void UnRegisterForNotification(
         const Cookie &cookie);

      void CheckForChange();

      CTime GetToday() const;

   private :

      CTime m_today;

      typedef std::map<Cookie, Listener *> Listeners;

      Listeners m_listeners;

      CCriticalSection m_criticalSection;

      // No copies - do not implement
      CDateChangeManager(const CDateChangeManager &rhs);
      CDateChangeManager &operator=(const CDateChangeManager &rhs);
};

DateChangeManager.cpp

CDateChangeManager::CDateChangeManager()
   :   m_today(CTime::GetCurrentTime())
{

}

CDateChangeManager::Cookie CDateChangeManager::RegisterForNotification(
   Listener &listener)
{
   CCriticalSection::Owner lock(m_criticalSection);

   Cookie cookie = reinterpret_cast<Cookie>(&listener);

   m_listeners[cookie] = &listener;

   return cookie;
}

void CDateChangeManager::UnRegisterForNotification(
   const Cookie &cookie)
{
   CCriticalSection::Owner lock(m_criticalSection);

   Listeners::iterator it = m_listeners.find(cookie);

   if (it != m_listeners.end())
   {
      m_listeners.erase(it);
   }
}

CTime CDateChangeManager::GetToday() const 
{
   return m_today;
}

void CDateChangeManager::CheckForChange()
{
   const CTime now = CTime::GetCurrentTime();

   struct tm tmNow;

   now.GetGmtTm(&tmNow);

   struct tm tmToday;

   m_today.GetGmtTm(&tmToday);

   if (tmNow.tm_year != tmToday.tm_year || 
       tmNow.tm_yday != tmToday.tm_yday)
   {
      CCriticalSection::Owner lock(m_criticalSection);

      const CTime then = m_today;

      m_today = now;

      for (Listeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
      {
         Listener *pListener = it->second;

         if (pListener)
         {
            pListener->OnDateChange(then, now);
         }
      }
   }
}