Simplify Your Unit Tests With Automocking: Part 2 – Establishing Context

Following my helper methods in the base context specification class that we use, I decided to simplify the entire process of setting the context in which the tests are running. Specifically, I wanted to get rid of the constant declaration and instantiation of the System Under Test (SUT) field – the class that is having it’s method called to ensure it behaves correctly.

 

A Base Context With SUT Setup

Instead of having to manually call out to the MockingKernel directly, to retrieve the system under test (SUT), like this:

   1: public class when_doing_something_with_that_thing : ContextSpecification

   2: {

   3:     protected MyPresenter SUT;

   4:     

   5:     protected override void EstablishContext()

   6:     {

   7:         SUT = Get<MyPresenter>();

   8:     }

   9:     

  10:     protected override void When()

  11:     {

  12:         SUT.DoSomething();

  13:     }

  14:     

  15:     [Test]

  16:     public void it_should_do_that_thing()

  17:     {

  18:         AssertWasCalled<IMyView>(v => v.ThatThing());

  19:     }

  20: }

I added a generics version of the ContextSpecification class to our set of spec helpers. I took the EstablishContext method out of the above code and dropped it into a ContextSpecification<T>:

   1: public class ContextSpecification<T>: ContextSpecification

   2: {

   3:     protected T SUT;

   4:     

   5:     override void EstablishContext()

   6:     {

   7:         SUT = MicroKernel.Get<T>();

   8:     }

   9: }

Now I can declare a spec without having to setup an EstablishContext method, if I don’t need one, and I don’t need to declare a protected SUT field:

   1: public class when_doing_something_with_that_thing : ContextSpecification<MyPresenter>

   2: {

   3:     protected override void When()

   4:     {

   5:         SUT.DoSomething();

   6:     }

   7:     

   8:     [Test]

   9:     public void it_should_do_that_thing()

  10:     {

  11:         AssertWasCalled<IMyView>(v => v.ThatThing());

  12:     }

  13: }

If I do need to set up additional code – stub methods, for example, I can still override the EstablishContext method. I just need to make sure I call to the base.EstablishContext so that I still get my SUT setup.

   1: protected override EstablishContext()

   2: {

   3:     base.EstablishContext();

   4:     Get<ISomeService>().Stub(v => v.ThisThingIsCalled()).Return("some value);

   5: }

 

A Complete Example

The complete example that yesterday’s post ended with is even easier to read, now. I’ve eliminated another chunk of code and the test gets straight to the heart of what is happening – the behavior of the system.

   1: public class when_doing_something_with_that_thing : ContextSpecification<MyPresenter>

   2: {

   3:     protected override void When()

   4:     {

   5:         SUT.DoSomething();

   6:     }

   7:     

   8:     [Test]

   9:     public void it_should_do_that_thing()

  10:     {

  11:         AssertWasCalled<IMyView>(v => v.ThatThing());

  12:     }

  13:  

  14:     [Test]

  15:     public void it_should_do_the_other_thing_twice()

  16:     {

  17:         AssertWasCalled<IMyView>(v => v.TheOtherThing(), mo => mo.Repeat.Twice());

  18:     }

  19: }

 

Other Considerations

I’m obviously very happy with what I’ve been able to do with the Ninject.RhinoMocks automocking container. However, there is a potential danger in using an auto mocking container. Stay tuned for tomorrow’s post to find out more about that danger and how you can help your team avoid it.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in .NET, AutoMocking, Behavior Driven Development, C#, Ninject, Refactoring, RhinoMocks, Unit Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://darrell.mozingo.net Darrell Mozingo

    Nice! I’ve done something pretty similar with our base context classes and it works out quite well, especially for some of the larger services and controllers that take in a handful of dependencies and basically delegate to them.

    You might also want to check out JP Boodhoo’s extensions to Machine.Specification: http://vimeo.com/11642767 . I haven’t moved our code base over yet, but I’m planning on it. All the goodness of Machine.Specification and pretty much exactly what you’ve done here (auto mocker abstraction, automatic SUT setup, etc), plus you don’t have to worry about the base context calls.

  • http://scottbellware.com Scott Bellware

    This is way outside the bounds of intended use of base class contexts in Context Specification. The use of the SUT abstraction stuff disqualifies this from being Context Specification for sacrificing usability characteristics and optimizing on programmer predilections like class abstraction and type reuse. Without the focus on usability and the optimization for the consumer mind as well as the producer mind, this style fails basic requirements for Context Specification. It’s a reasonable facsimile of the coding pattern, but that’s where it ends for the most part.

    I’ve busted JP for doing this as well, and for not seeing into the part of the spectrum where the consumer’s productivity is sacrificed to the producer’s mental box.

    It dramatically misses the point. See beyond the programmer stuff. There’s way more to it than this parametrized abstraction stuff, and there’s yet more levels of understanding with yet greater returns.

    On the automocker stuff: The only reason that an automocker becomes necessary is when module design is too chunky. This is usually a result of using a framework that drives the granularity of layer archetypes, like controllers, etc. The presence of an automocker indicates design problems somewhere else. The use of an automocker is a solution to a specific kind of test friction that can be solved by changing the design so that it’s no longer needed.

    That said, some frameworks can impose design constraints on layer archetypes that you simply can’t resolve. At that point, and automocker might be just what the doctor ordered, but it’s no more desirable from a productive design perspective than mocking through profiler interception.

    In fact, there’s little difference at that level between using an automocker or TypeMock. Ultimately, the underlying design problem is not being seen and realized, and it’s being glossed over rather than solved. Greater whole productivity will come from solving the underlying structural design issues, if the frameworks being used allow for it.

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    how does SUT disqualify it from being context/spec? … honest question… trying to understand what i’m missing.

    i agree that automockers can allow those problems to exist, but saying that they cause those problems is ridiculous. automockers are no on the same level of fallacy as drag & drop, though i do see the parallel to typemock, which make me question things.

    … tomorrow’s blog post basically just repeats what i’ve already said. i bring it up because i wrote it on sunday, at the same time that i wrote these 2 parts… so it’s going to be much of the same discussion we’ve already had.

    i’m just not convinced that an automocker is the cause of problems, or _always_ hides problems… yes i see that it can hide problems, but only when a developer doesn’t know how to recognize those problems in the implementation.