The motive of this post is not to prove that setter injection is better than constructor injection. But being setter the underdog I am surprised how much it has helped in defining my style of development. So despite agreeing that is safer to always create a sound object, I get to write less production code and more expressive tests with the setter strategy.
The previous post laid the ground rules and general ideas behind why setter injection has simplified my code. Looking into Fubu MVC's code I found a nice example to illustrate what I mean. It's kinda funny because it was a post by Jeremy Miller what inspired me to look into Fubu's implementation. In that post he evens jokes about Chad using setter injection.
Here is the Test
[TestFixture]
public class execute_one_in_one_out : InteractionContext<OneInOneOutActionInvoker<ITargetController, Input, Output>>
{
private Input theInput;
private Output expectedOutput;
protected override void beforeEach()
{
Func<ITargetController, Input, Output> func = (c, i) => c.OneInOneOut(i);
Services.Inject(func);
theInput = new Input();
expectedOutput = new Output();
MockFor<IFubuRequest>().Expect(x => x.Get<Input>()).Return(theInput);
MockFor<ITargetController>().Expect(x => x.OneInOneOut(theInput)).Return(expectedOutput);
ClassUnderTest.Invoke();
}
[Test]
public void should_have_stored_the_resulting_data_in_the_fubu_request()
{
MockFor<IFubuRequest>().AssertWasCalled(x => x.Set(expectedOutput));
}
[Test]
public void should_invoke_the_controller_method()
{
VerifyCallsFor<ITargetController>();
}
}
and the Code
public class OneInOneOutActionInvoker<TController, TInput, TOutput> : BasicBehavior where TInput : class
where TOutput : class
{
private readonly Func<TController, TInput, TOutput> _action;
private readonly TController _controller;
private readonly IFubuRequest _request;
public OneInOneOutActionInvoker(IFubuRequest request, TController controller,
Func<TController, TInput, TOutput> action)
{
_request = request;
_controller = controller;
_action = action;
}
// TODO: Harden against failures?
protected override DoNext performInvoke()
{
var input = _request.Get<TInput>();
TOutput output = _action(_controller, input);
_request.Set(output);
return DoNext.Continue;
}
\
Here is how I would write Test
[TestFixture]
public class when_executing_one_in_one_out : BehaviorOf<OneInOneOutActionInvokerWithSetters<ITargetController, Input, Output>> {
[Test]
public void should_have_stored_the_resulting_data_in_the_fubu_request() {
var Input = new Input();
var Output = new Output();
Given.Action = (c, i) => c.OneInOneOut(i);
Given.Request.Get<Input>().Is(Input);
Given.Controller.OneInOneOut(Input).Is(Output);
When.PerformInvoke();
Then.Request.Should().Set(Output);
}
}
and the Code
public class OneInOneOutActionInvokerWithSetters<TController, TInput, TOutput> : BasicBehavior
where TInput : class where TOutput : class {
public Func<TController, TInput, TOutput> Action { get; set; }
public TController Controller { get; set; }
public IFubuRequest Request { get; set; }
public override DoNext PerformInvoke() {
var Input = Request.Get<TInput>();
var Output = Action(Controller, Input);
Request.Set(Output);
return DoNext.Continue;
}
\
In both cases the TextFixture inherits from a base class. This is the Testcase Superclass pattern from Meszaros book. The idea is to establish a context for the SUT and simplify DI and mocking. In Fubu, InteractionContext provides a facade around Rhino Mocks enabling automocking and simplifying expectations calls. It also wraps the SUT with ClassUnderTest.
In my case BehaviorOf is the same pattern built into FluentSpec. The SUT dependencies are automocked and the SUT is wrapped. But instead of accessing the SUT through ClassUnderTest it's accessed through Given/When/Then connectors. As a result I would not need to implement InteractionContext.
Fubu's test was refactored into a common setup pattern which enables to have a single line per test and that line is an assertion. That is test nirvana. However the second test case does a VerifyCallsFor<TestDouble> which is a test smell. Therefore my test has a single assertion and there was no point in refactoring to common setup.
Because of the public dependencies instead of.
Func<ITargetController, Input, Output> func = (c, i) => c.OneInOneOut(i);
Services.Inject(func);
MockFor<IFubuRequest>().Expect(x => x.Get<Input>()).Return(theInput);
MockFor<ITargetController>().Expect(x => x.OneInOneOut(theInput)).Return(expectedOutput);
MockFor<IFubuRequest>().AssertWasCalled(x => x.Set(expectedOutput));
I can write.
Given.Action = (c, i) => c.OneInOneOut(i);
Given.Request.Get<Input>().Is(Input);
Given.Controller.OneInOneOut(Input).Is(Output);
Then.Request.Should().Set(Output);
Which is simpler, more expressive and no lambdas or brackets were harmed in vain. Also notice how clear is the separation between the test steps Setup/Exercise/Verify mapped to Given/When/Then than with the AAA syntax.
The Exercise step changed from ClassUnderTest.Invoke() to When.PerformInvoke(). In this case When is a win over ClassUnderTest but making PerformInvoke public is a loss. The tradeoff in my case boils down to focus. I want to exercise the essential amount of code related to the behavior being tested. So calling Invoke goes through different path of the code that I don't care while defining when_executing_one_in_one_out.
VerifyCallsFor<ITargetController> was not needed because it is ensuring that the expectations were executed for ITargetController. In this case MockFor<ITargetController>().Expect(x => x.OneInOneOut(theInput)).Return(expectedOutput) is a query. That happens mostly as a misuse of AAA syntax with Query/Command. Queries belong to the setup step and it feels silly to check AssertWasCalled on a query statement.
Queries can be verified either by checking affected state in the SUT, via the return value in a delegated query or because their result becomes an arg in a command call. We don't need to verify that Controller.OneInOneOut(Input) occurred because that is what ensures that Then.Request.Should().Set(Output) Given.Controller.OneInOneOut(Input).Is(Output).
Encapsulation is one of the fundamental concepts that makes OOP work. Sadly, it reinforces the human feeling of protection. And we do the most silly things in the name of protection. By letting go on class encapsulation in favor of abstract dependencies I have developed a style that leads to cleaner tests. Setter injection has opened the opportunity for the dependencies to join fluently in the chain of specifications.
I'd rather have everything but that's impossible. The tradeoffs you make are related to your personality. Because I favor expressiveness a lot more than security I was able to suspend beliefs and let go. I am much happier with more expressive tests than with the security that constructor injection offers. But that's me, you have your own rules to make, break and follow.