So I was recently at a good friend's birthday gig. He's actually my old lead at Microsoft. Anyways, some of our friends that are still at Microsoft were at the party and I got a chance to talk to one of them for a while about various things... primarily open source, TDD, Inversion of Control and Mock objects. He was interested in Rhino Mocks and wanted me to email him about it. Instead of just doing that I threw together a good sized email. Here it is (slightly modified):
Good to see you again. As we talked about, there has been quite a shift in the way we write programs... that is a shift towards a much more testable, more maintainable type of programming. I know there are teams at Microsoft that have embraced recent changes... some have gone agile, most use some form of continuous integration, and some have written Inversion of Control containers (the Composite UI Application Block by the patterns & practices group has a container in it), but I'm sure most teams aren't employing all of these practices and could benefit from at least some of them.
In a number of teams, I think that there are a lot of improvements that can be made... even without shipping anything that's open source.
Firstly, you should take another serious look at Test Driven Development. I've attached an interesting study on TDD, and you may want to skim the MS Press book on the subject.
Also, the following blogs have some real good info on it:
Jeremy D. Miller's TDD Posts
Ayende's TDD Posts
A big part of TDD and Unit Testing in general is being able to remove not only your ephemeral dependencies, but also the rest of your dependencies. That way you know, when something fails, exactly what class caused the failure... it's the class being tested. In order to get rid of the dependencies, you need to replace them. In order to replace them, you need two things. The first is a replacement. The second is a method in which to inject the replacement. Mocks and stubs can handle the first task. I mentioned that we use Rhino Mocks:
Ayende's Rhino Mocks Posts
The library is great, it makes for very readable code and it's pretty darn flexible.
The second item can be a bit more complicated. The thing you usually see when class Foo depends on class Bar is this:
class Foo
{
private Bar _bar = new Bar();
public string DoStuff()
{
return _bar.DoSomeOtherStuff() ? "yes" : "no"
}
}
Now if you're going to test Foo, you'll also be testing Bar. You can't avoid that the way it's written. Now, it's good to test Foo talking to Bar eventually, but that's what integration tests are for... not Unit tests. So what do you do? This:
interface IBar { bool DoSomeOtherStuff(); }
class Foo
{
private IBar _bar;
public Foo(IBar bar) { _bar = bar; }
public string DoStuff()
{
return _bar.DoSomeOtherStuff() ? "yes" : "no"
}
}
Subtle difference, but this allows you to throw any implementation of IBar (including a mock) into Foo. That allows you to write a test like this:
[Test]
void DoStuff_WhenBarReturnsTrue_ReturnsYes()
{
MockRepository mocks = new MockRepository();
IBar bar = mocks.CreateMock<IBar>();
Foo foo = new Foo(bar);
Expect.Call(bar.DoSomeOtherStuff()).Returns(true);
mocks.ReplayAll();
Assert.Equals("yes", foo.DoStuff());
}
Now even if Bar changes, or Bar doesn't even exist, this test will still pass.
Here's the definition from Martin Fowler of Dependency Injection/Inversion of Control
Unfortunately, real programs are a lot more complicated than just one class depending on another... dependency chains are generally pretty deep, and one class can depend on 5 classes, each of which depends on 2, each of which has a different lifetime... per web request, transient, singleton, etc. It would be quite a pain if you had to instantiate everything like this:
Foo foo = new Foo(new Bar(new Blah(), Yadda.Instance));
Fortunately, it's not terribly complicated to write a Container that can handle all of this for you... so all you do is something like:
Foo foo = container.Resolve<Foo>();
There are several:
And then I went on to mention how long this mail was getting and that I was going to post it to this blog.