Wednesday 14 January 2015

Verifying interaction with 3rd-party frameworks via tests - Castle Windsor

I recently developed a simple class to process Commands. The CommandProcessor class takes advantage of Castle Windsor's excellent Typed Factory Facility. In essence, the CommandProcessor depends on a CommandFactory to resolve and release instances of Command objects that it executes.

As part of the testing of this class, I wanted to ensure that I could verify that the Command objects were being resolved and released by Castle Windsor as I expected. It's not that I don't trust how Castle Windsor behaves but I like my tests to serve as explicit statements of how I have designed my code to work. Too many times I've looked at code, my own included, and questioned the true intent. Furthermore, I like my tests to prevent unwitting changes that will negatively affect the code's behaviour. Considering operations like resolving and releasing object instances via IoC, these negative effects might prove very subtle and difficult to debug.

My test takes advantage of Castle Windsor's support for events:
container.Register(
    Component.
        For(Of ICommand)().
        UsingFactoryMethod(Function(k) Substitute.For(Of ICommand)()).
        LifestyleTransient().
        OnCreate(Sub(c) componentWasCreated = True).
        OnDestroy(Sub(c) componentWasReleased = True).
        IsDefault())

I register a fake command with the container and tell it that when it creates and destroys this command, it should set local variables so that I can assert against them.

Then I go on to resolve the processor and execute the command:
Dim processor = container.Resolve(Of ICommandProcessor)()
 
processor.Execute(Of ICommand)()

Finally, I assert against the variables that should've been set when Castle Windsor raised the appropriate events:
Assert.That(componentWasCreated, [Is].True)
Assert.That(componentWasReleased, [Is].True)

The test in full:
<Test()>
Public Sub Execute_IocContainerConfiguredByApplication_ShouldResolveAndReleaseCommand()
 
    Dim container = Program.CreateIoCContainer()
 
    Dim componentWasCreated As Boolean
    Dim componentWasReleased As Boolean
 
    container.Register(
        Component.
            For(Of ICommand)().
            UsingFactoryMethod(Function(k) Substitute.For(Of ICommand)()).
            LifestyleTransient().
            OnCreate(Sub(c) componentWasCreated = True).
            OnDestroy(Sub(c) componentWasReleased = True).
            IsDefault())
 
    Dim processor = container.Resolve(Of ICommandProcessor)()
 
    processor.Execute(Of ICommand)()
 
    Assert.That(componentWasCreated, [Is].True)
    Assert.That(componentWasReleased, [Is].True)
 
End Sub

Previously, I might not have attempted to write this kind of test, assuming that the code was simple enough not to warrant verifying its behaviour. However, when frameworks offer the appropriate interaction points that allow test verification, and when we start to look at tests as more than just things that verify behaviour, it becomes clear that these kind of tests offer a significant degree of value.

No comments:

Post a Comment