Using TDD to learn new code
When I pick up a new framework or library, there's usually that learning curve where I get familiar with its API, find what works, what doesn't work, etc. One habit I've gotten into is that I create a TestFixture
for everything i think i should be able to do and build a test for that assumption. The purpose of these tests is both to make sure the code does what I expect it to, but also to serve as a record of what I've already learned. If i later on wonder how some call would function, i first check my test signatures, to see if i've already tested that behavior. If there is an appropriate test, I immediately know what the behavior will be, plus i now have working sample code or how to do it.
For example, I was playing around with setting up Moq's through Autofac and wanted to come up with a registration that would give me a container scoped Moq object that i could set up before executing a particular test. The resulting test looked like this:
public interface IMockWithAccessor
{
IMockAccessorValue Accessor { get; }
}
public interface IMockAccessorValue
{
string Foo { get; }
}
[Test]
public void Create_nested_mock_so_it_can_be_altered_in_container_scope()
{
var builder = new ContainerBuilder();
builder.Register(c => new Mock<IMockAccessorValue>())
.As<Mock<IMockAccessorValue>>().ContainerScoped();
builder.Register(c => c.Resolve<Mock<IMockAccessorValue>>().Object)
.As<IMockAccessorValue>().ContainerScoped();
builder.Register(c =>
{
var mockBuilder = new Mock<IMockWithAccessor>();
mockBuilder.Setup(x => x.Accessor)
.Returns(c.Resolve<IMockAccessorValue>());
return mockBuilder.Object;
}).As<IMockWithAccessor>().ContainerScoped();
using (var container = builder.Build().CreateInnerContainer())
{
var mockAccessorBuilder = container
.Resolve<Mock<IMockAccessorValue>>();
mockAccessorBuilder.Setup(x => x.Foo).Returns("bar");
Assert.AreEqual("bar", container
.Resolve<IMockWithAccessor>().Accessor.Foo);
}
}
Sometimes, of course, my expectations are not met and the code does not allow me to do what i set out to do. These test are even more valuable for future reference, as long as i make sure to rename the test to reflect the failed expectation, and alter the asserts to reflect the actual behavior.
I was trying to figure out parametrized component registrations in Autofac. The example showed it being used with FactoryScope
. I wondered whether, in default (Singleton) scope, Autofac would use the parameters to create singletons per parameter set. My original test was named Parametrized_resolve_creates_different_singleton_per_parameter_Value. Well, it turned out that, no, autofac does not vary singletons, and parametrized registrations only make sense in FactoryScope
. The final test looks like this:
public class ParametrizedSingleton { }
[Test]
public void Parametrized_resolve_without_factory_scope_is_always_a_singleton()
{
var builder = new ContainerBuilder();
builder.Register((c, p) => new ParametrizedSingleton());
using (var container = builder.Build())
{
var foo1 = container.Resolve<ParametrizedSingleton>(
new NamedParameter("type", "foo"));
var foo2 = container.Resolve<ParametrizedSingleton>(
new NamedParameter("type", "foo"));
var bar1 = container.Resolve<ParametrizedSingleton>(
new NamedParameter("type", "bar"));
Assert.AreSame(foo1, foo2);
Assert.AreSame(foo1, bar1);
}
}
I usually keep these test fixtures in a separate test project in my solution as permanent reference, as i continue to develop the main code. It's proven useful a number of times when coming back to some old code and having to reacquaint myself with a third party bit of code.