In a comment on his posting Testing the Mock, Aslak Hellesoy points out that one of the solutions is less than perfect because it breaks encapsulation to allow testing. That's true, but sometimes the right thing to do is bend the rules so you can test.
In Aslak's example, the twist was to introduce a factory method for the creation of new instances of an internal object.
public void testExecuteAddsCheeseDescriptionToDao() {
final Mock mockDao = new Mock(CheeseDao.class);
mockDao.expect("saveCheese", C.eq("stilton"));
CheeseAction action = new CheeseAction() {
// ---- override ----
protected CheeseDao newDao() {
return ((CheeseDao)mockDao.proxy());
}
};
action.setCheese("stilton");
assertEquals(ActionSupport.SUCCESS, action.execute());
mockDao.verify();
}
It's true that we're exposing a method just for testing, but it's a step in the right direction because it starts to isolate the creation of new instances within our code. Later on, once we see how the construction patterns pan out, this might turn into a full-blown factory object. In the meantime, this gets around the chore of creating a new factory class each time (1).
The situation is more severe in the C# world because so much of the framework code is locked down. For example, you want to write unit tests for code that accesses web session state? Tough. Everything's sealed. The solution my last team came up with was to wrap the built-in session object in a thin veneer(2).
public class CheeseSession : ICheeseSession {
private Session session;
public CheeseSession(Session session) { this.session = session; }
public string Variety {
get { return (string) Session.Properties["variety"]; }
}
}
We couldn't find a way to unit test CheeseSession, so we just kept it so simple that it was obviously right.
Then we had a base Form class that returns a CheeseSession when we needed one: new instances for now, and maybe cached with the session later.
public class CheeseForm : WebForm {
protected virtual ICheeseSession CheeseSession {
get { return new CheeseSession(Session); }
}
}
When unit-testing forms, we can do the "subclass and override" trick:
public class StubCheeseActivityForm : CheeseActivityForm {
public ICheeseSession mockCheeseSession;
protected override ICheeseSession CheeseSession {
get { return mockCheeseSession; }
}
}
[TestFixture] public classs CheeseActivityFormTest {
[Test] public void SmellyCheesesAreDoubleWrapped() {
Mock mockWrapper = new Mock(typeof(CheeseWrapper));
Mock mockSession = new Mock(typeof(CheeseSession));
StubCheeseActivityForm form = new StubCheeseActivityForm((CheeseWrapper)mockWrapper.MockInstance);
form.mockCheeseSession = (ICheeseSession)mockSession.MockInstance;
mockSession.setupResult("Variety", "gorgonzola");
mockWrapper.expect("LayerCount", 2);
form.OnCommit(); // or whatever the call is
mockWrapper.Verify();
}
}
Is this ugly? It sure is, but there aren't many options if you want to unit test .Net web form code without starting up a server. I also really missed Java's anonymous classes for minimizing code. That's the bad news. On the other hand, it forced us to clarify the interface between our domain (Cheese) and our platform (Web Forms), and that interface is realized exactly once, in the CheeseSession property. This turned out to have the nice property that we didn't have to commit early to a mechanism for preserving session state because we were confident that we could easily change our minds.
With a little attention and a sprinkling of interfaces, we found that our code was moving towards a structure where all the important code was defined in terms of the domain rather than the platform. We almost rediscovered the old lisp motto about writing a domain language and then writing your application in that language.