Example: a seam in a C++ function
bool CAsyncSslRec::Init() {
if (m_bSslInitialized) {
return true;
}
m_smutex.Unlock();
m_nSslRefCount++;
m_bSslInitialized = true;
FreeLibrary(m_hSslDll1);
m_hSslDll1=0;
FreeLibrary(m_hSslDll2);
m_hSslDll2=0;
if (!m_bFailureSent) {
m_bFailureSent=TRUE;
PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);
}
CreateLibrary(m_hSslDll1,"syncesel1.dll");
CreateLibrary(m_hSslDll2,"syncesel2.dll");
m_hSslDll1->Init();
m_hSslDll2->Init();
return true;
}Suppose we want to run all of Init() except this line:
PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);We can’t just delete it — it has to stay in production. PostReceiveError is a global function that talks to another subsystem that’s painful under test. So the real problem is: how do we run the method without calling PostReceiveError in a test, while still calling it in production? That question is what leads to the idea of a seam.
A seam is a place where you can alter behavior in your program without editing in that place.
Is there a seam at the PostReceiveError call? Yes. Because PostReceiveError is a global function (not part of CAsyncSslRec), we can add a method with the same signature to the class that just delegates to it via C++‘s scoping operator:
class CAsyncSslRec {
...
virtual void PostReceiveError(UINT type, UINT errorcode);
...
};void CAsyncSslRec::PostReceiveError(UINT type, UINT errorcode) {
::PostReceiveError(type, errorcode);
}Behaviour is preserved — there’s a little indirection, but production still calls the same global function. Now subclass and override the new method to do nothing:
class TestingAsyncSslRec : public CAsyncSslRec {
virtual void PostReceiveError(UINT type, UINT errorcode) {}
};If our tests instantiate TestingAsyncSslRec instead of CAsyncSslRec, the call to PostReceiveError inside Init() becomes a no-op, and we can test the method without the nasty side effect.
This is an object seam: we changed which method runs without editing the method that calls it. Object seams are available in object-oriented languages, and they’re just one of several kinds of seams the book covers.
Source
Working Effectively with Legacy Code, Michael Feathers — Chapter 4, The Seam Model.