In the last part, we came up with a crude and useful method for intercepting calls to the Windows API by stuffing virtual methods which otherwise have the same signature as the API in base classes. This works well for one-off cases, but for heavier use, it suffers from some problems:
-
You need to write the intercept method into every inheritance hierarchy you need to test.
-
You need to derive from every class that you want to test.
-
The APIs you need to intercept often come in groups.
Whew! That’s a lot of work, and a lot of boiler plate code to break your fingers on. Let’s start tweaking our method a bit. Say we have a class like this:
class CriticalSectionAPI {
public:
virtual ~CriticalSectionAPI() {
}
virtual void InitializeCriticalSection(LPCRITICAL_SECTION lpCS) const {
::InitializeCriticalSection(lpCS);
}
virtual void EnterCriticalSection(LPCRITICAL_SECTION lpCS) const {
::EnterCriticalSection(lpCS);
}
// other critical-section related methods ...
};
We can inject this into an inheritance hierarchy easily enough using inheritance:
class Foo : public Bar, protected CriticalSectionAPI {
public:
Foo(int iArg)
{
}
// ...
};
I’m using protected inheritance so we don’t expose this as part of the object’s API.
The above removes the necessity to write all the intercepts, but you still need to derive from every tested class. Let’s refactor a bit and extract an interface:
struct ICriticalSectionAPI { virtual ~ICriticalSectionAPI() {} virtual void InitializeCriticalSection(LPCRITICAL_SECTION lpCS) const = 0; // ... }; class RealCriticalSectionAPI : public ICriticalSectionAPI { public: virtual void InitializeCriticalSection(LPCRITICAL_SECTION lpCS) const { ::InitializeCriticalSection(lpCS); } // ... }; class TestableCriticalSectionAPI : public ICriticalSectionAPI { public: TestableCriticalSectionAPI(boost::shared_ptr<ICriticalSectionAPI> pAPI) : m_pAPI(pAPI) { } virtual void InitializeCriticalSection(LPCRITICAL_SECTION lpCS) const { m_pAPI->InitializeCriticalSection(lpCS); } // ... private: boost::shared_ptr<ICriticalSectionAPI> m_pAPI; }; class Foo : public Bar, protected TestableCriticalSectionAPI { public: Foo(int iArg, boost::shared_ptr<ICriticalSectionAPI> pAPI = boost::shared_ptr<ICriticalSectionAPI>(new RealCriticalSectionAPI) ) : TestableCriticalSectionAPI(pAPI) { } };
The key points here are:
-
We’ve created a
TestableCriticalSectionAPI
which simply delegates to an API implementation of our choice. -
We’ve introduced a default parameter into the class under test’s constructor to select our API implementation.
-
Since we are using an new default parameter, and since it is a smart pointer (which isn’t directly convertible to a pointer), we can be pretty sure that we’ve maintained the source-level interface to the class. We can be sure the compiler will tell us if we’re wrong.
Now that we have that done, we can notice that we have a major DRY violation. The API’s signature is encoded in five different places, not counting overrides for testing! That’s a whole lot of redundant typing! While we have saved quite a bit of finger-numbing work for the use of a mocked API, we’ve really moved that finger-number work to the implementation of the mocked API. This is a positive change when the API is used more than once, but we can still do more.
In the next installment, I can either cover the use of some dirty preprocessor tricks to get rid of this DRY violation, or I can write about an alternate template-based method for injecting a mock API. Remember the choose-your-own-adventure books? Which topic do you want to see next? Vote in the comments.