Making Machine.Specifications less ‘machiney’

I have been a user of Machine.Specifications or Mspec for awhile now, and have been pretty happy with how much more readable it makes my unit testing.

If you are not familiar with Mspec it is a testing framework for context/specification that uses delegates to setup the Act, Arrange, & Assert pattern.

There are several good getting started guides if this is new to you, but here is a basic example of the structure.

    public class When_a_content_admin_asks_for_a_non_existent_page
    {
        Establish that;

        Because of;

        It Should_check_the_repository;

        It Should_present_a_blank_add_content_screen;

        It Should_auto_populate_the_url_field;
    }
	
Now of course to do unit testing you must be able to do mocking/stubbing/faking etc…, I am normally a user of Moq and that is what I use in my Mspec test to establish my mocks. While it works well, all the noise it takes to arrange all of my mocks at times bothers me. While the noise was irritating I just dealt with it because setting up mocks is just something you have to do, but luckily I recently discovered somebody else had the motivation to
actually do something about it.
 
 
 
Machine.Fakes is a compliment to Machine.Specifications and reduces the obligatory mocking and dependency injection code in your tests and replaces it with a bit of magic. Machine.Fakes itself does not depend on a particular mocking framework, it uses a plugin style that lets you use any framework of your choosing. As I write this the options available (on NuGet) are Moq, FakeItEasy, Rhino.Mocks, & NSubstitute.
 
Here is an example of a simple MVC controller test of mine using just MSpec & Moq.
public class When_a_content_admin_asks_for_a_non_existent_page
    {
        Establish that = () =>
        {
            var auth = new Mock<Authorization>();
            auth.Setup(x => x.IsContentAdmin(GivenIt.IsAny<HttpContextBase>()))
                .Returns(true);

            _repo = new Mock<ContentRepository>();
            _repo.Setup(x => x.Exists("a/bogus/url"))
                .Returns(false);

            _controller = new MeekController(_repo.Object, auth.Object);
        };

        Because of = () =>
            _result = _controller.Manage("/a/bogus/url");

        It Should_check_the_repository = () =>
            _repo.Verify(x => x.Exists("a/bogus/url"), Times.Once());

        It Should_present_a_blank_add_content_screen = () =>
            _result.AssertViewRendered().ForView("Manage");

        It Should_auto_populate_the_url_field = () =>
            (_result.AssertViewRendered().Model as Content.Manage).ManageUrl.ShouldNotBeEmpty();

        static ActionResult _result;
        static MeekController _controller;
        static Mock<ContentRepository> _repo;
}
	
 

You can see that I am Mocking both an Authorization and a ContentRepository interface, with the ContentRepository defined as a field so I can assert upon it later. Both mocks are passed into the class constructor of my system under test (SUT). Now while this is not a ton of noise we can trim it down to a more readable state with Machine.Fakes, below is my updated test.

public class When_a_content_admin_asks_for_a_non_existent_page : WithSubject<MeekController>
    {
        Establish that = () =>
            {
                The<Authorization>()
                    .WhenToldTo(x => x.IsContentAdmin(GivenIt.IsAny<HttpContextBase>()))
                    .Return(true);

                The<ContentRepository>()
                    .WhenToldTo(x => x.Exists("a/bogus/url"))
                    .Return(false);
            };

        Because of = () =>
            _result = Subject.Manage("/a/bogus/url");

        It Should_check_the_repository = () =>
            The<ContentRepository>().WasToldTo(x => x.Exists("a/bogus/url")).OnlyOnce();

        It Should_present_a_blank_add_content_screen = () =>
            _result.AssertViewRendered().ForView("Manage");

        It Should_auto_populate_the_url_field = () =>
            (_result.AssertViewRendered().Model as Content.Manage).ManageUrl.ShouldNotBeEmpty();

        static ActionResult _result;
    }
	
There are a few things to point out
  • First thing to notice is that I am inheriting from a WithSubject<> base class, this is where you specify your SUT.
  • Under the Establish we are no longer newing up any concrete mocks we are only telling Machine.Fakes of our expectations with the The<> object.
  • Without an instance of a mock you will now be using the Machine.Fakes method for inspecting if a call was made or not with the same The<> object used to setup it up.
  • Any place that you had been referencing your SUT you will replace with the Subject property.
  • Since we are no longer declaring an instance of our mocks or SUT we can remove the field declarations.
  • I am still using the GivenIt.IsAny<HttpContextBase>() from my Moq framework to establish the parameter expectation.
 
It did not reduce the lines of code a ton but but this is a pretty lean test to begin with, the important part is that we removed most of the plumbing code that had nothing to do with setting expectations.

 

Conclusion… I am a happy camper that has to type even less now.

Did you like this? Share it:
Tags:  , ,

Comments

Posted On
Mar 17, 2011
Posted By
Phil Ledgerwood

This was great, Brian. I’ve been trying to de-machine MSpec, and this was very helpful. I’ve been using FakeItEasy lately for my mocking needs, and it has been much more readable than, say, Moq, but I think I’ll give this a whirl and see how I like it.

Posted On
Mar 17, 2011
Posted By
Brian Wigfield

I just updated my code samples I did not realized when I pasted them in it turned all of my generics into HTML tags.

Leave a Reply


Recent tweets