moving from nunit to machine.specifications

Making the transition from NUnit tests can be hard for a team that is invested in that style of testing and have been doing that for a number of years. The foreign nature of a context specification style testing framework and seeing a mass of delegate declarations can prove a barrier for migration, so here's my recommendation for making a smoother transition between the two.
I'll be illustrating how to move from a typical NUnit style of testing to using Machine.Specifications.

First, we need to examine how tests are currently being written in NUnit. A typical test may look something like this fictitious class:

    [TestFixture]
    public class FileInspectorTests
    {
        IFileInspector fileInspector;
        FakeFileReader fileReader;

        public void TestCallsReader()
        {
            fileReader = new FakeFileReader();
            fileReader.FakeContent = "fake content for the test";
            fileInspector = new FileInspector(fileReader);
            fileInspector.InspectFile("filename");
            Assert.IsTrue(fileReader.GetContentWasCalled);
        }

        [Test]
        public void TestFindsSecondWord()
        {
            fileReader = new FakeFileReader();
            fileReader.FakeContent = "first second third fourth";
            fileInspector = new FileInspector(fileReader);
            fileInspector.InspectFile("filename");
            Assert.AreEqual("second", fileInspector.SecondWord);
        }

        public class FakeFileReader : IFileReader
        {
            public string GetContentFromFile(string file)
            {
                GetContentWasCalled = true;
                return FakeContent;
            }

            public bool GetContentWasCalled { get; set; }            

            public string FakeContent { get; set;}
        }
    }

Here we have a few simple tests and these are of a common pattern for NUnit (in my experience). We'll be looking at shaping this into a better NUnit test first before we consider moving to another framework.

  • Step one : use a mocking framework instead of simple hand rolled stubs

The simple hand rolled stub above is the FakeFileReader and this is just extra code that isn't required. Today's mocking frameworks are very flexible and can provide proxies for our abstractions without us having to create new classes.
Also, many mocking frameworks utilise lambdas which is a great step towards using Machine.Specifications.
Here's an example using Rhino Mocks:

    [TestFixture]
    public class FileInspectorTests
    {
        IFileInspector fileInspector;
        IFileReader fileReader;

        public void TestCallsReader()
        {
            var fileName = "filename";
            var fakeContent = "fake content for the test";

            fileReader = MockRepository.GenerateStub<IFileReader>();            
            fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);

            fileInspector = new FileInspector(fileReader);
            fileInspector.InspectFile("filename");
            
            fileReader.AssertWasCalled(x=> x.GetContentFromFile(fileName));
        }

        [Test]
        public void TestFindsSecondWord()
        {
            var fileName = "filename";
            var fakeContent = "first second third fourth";

            fileReader = MockRepository.GenerateStub<IFileReader>();
            fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);

            fileInspector = new FileInspector(fileReader);
            fileInspector.InspectFile("filename");
            Assert.AreEqual("second", fileInspector.SecondWord);
        }
    }

A stub is created and we stub the method to return the content, finally asserting that the stub recorded a call to the method with the correct parameters.
No extra class, and two lambdas to introduce familiarity with delegates - all good so far.

  • Step Two : move to a contextual style of testing - the AAA pattern

There is a testing pattern called the AAA pattern that enables you to separate set up, system under test execution and your assertions from each other. It stands for Arrange, Act, Assert.
By introducing a base class for this more contextual style we can follow this pattern when NUnit testing:

    [TestFixture]
    public class FileInspectorTests : ContextTest
    {
        IFileInspector fileInspector;
        IFileReader fileReader;
        string fileName;
        string fakeContent;

        protected override void Arrange()
        {
            fileName = "filename";
            fakeContent = "fake content for the test";
            fileReader = MockRepository.GenerateStub<IFileReader>();
            fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);
            fileInspector = new FileInspector(fileReader);
        }

        protected override void Act()
        {
            fileInspector.InspectFile("filename");
        }

        [Test]
        public void TestCallsReader()
        {
            fileReader.AssertWasCalled(x=> x.GetContentFromFile(fileName));
        }

        [Test]
        public void TestFindsSecondWord()
        {
            Assert.AreEqual("second", fileInspector.SecondWord);
        }
    }

    public abstract class ContextTest
    {
        [TestFixtureSetUp]
        public void SetUp()
        {
            Arrange();
            Act();
        }

        protected abstract void Arrange();

        protected abstract void Act();
    }

This makes our test much cleaner - we've removed duplication of the setup and the system under test execution and more importantly we can clearly see what is being tested; there is a single line in the Act() method that illustrates what is being executed.

  • Step Three : use contextual descriptions for tests classes and assertive methods

The structure of the test above is great, but we can improve upon this layout by simple describing our tests with intuitive naming on the class and the methods. Keeping in the NUnit style, my recommendation is that you move to something like the following:

    [TestFixture]
    public class FileInspectorTest_WhenInspectingAFileFromAFileName : ContextTest
    {
        IFileInspector fileInspector;
        IFileReader fileReader;
        string fileName;
        string fakeContent;

        protected override void Arrange()
        {
            fileName = "filename";
            fakeContent = "fake content for the test";
            fileReader = MockRepository.GenerateStub<IFileReader>();
            fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);
            fileInspector = new FileInspector(fileReader);
        }

        protected override void Act()
        {
            fileInspector.InspectFile("filename");
        }

        [Test]
        public void ShouldTellTheFileReaderToObtainTheContentFromTheFile()
        {
            fileReader.AssertWasCalled(x=> x.GetContentFromFile(fileName));
        }

        [Test]
        public void ShouldFindTheCorrectSecondWord()
        {
            Assert.AreEqual("second", fileInspector.SecondWord);
        }
    }

This test now hopefully rolls off the tongue a bit better - "it is a file inspector test and when inspecting a file from a file name it should tell the file reader to obtain the content from the file and it should find the correct second word".

  • Step Four : illustrate to the team how an Nunit test could morph to a delegate driven framework

I've found that to get to grips with the delegate driven nature of Machine.Specification it is helpful to illustrate how we can move the above test to using strong delegates and we effectively morph the test into a Machine.Specification style test.
We begin this step by moving the arrange and act into Action delegates:

    [TestFixture]
    public class FileInspectorTest_WhenInspectingAFileFromAFileName : ContextTest
    {
        static IFileInspector fileInspector;
        static IFileReader fileReader;
        static string fileName;
        static string fakeContent;

        Action arrange = () =>
         {
             fileName = "filename";
             fakeContent = "fake content for the test";
             fileReader = MockRepository.GenerateStub<IFileReader>();
             fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);
             fileInspector = new FileInspector(fileReader);
         };

        Action act = () =>
         {
             fileInspector.InspectFile("filename");
         };

        protected override void Arrange()
        {
            arrange();
        }

        protected override void Act()
        {
            act();
        }

        [Test]
        public void ShouldTellTheFileReaderToObtainTheContentFromTheFile()
        {
            fileReader.AssertWasCalled(x=> x.GetContentFromFile(fileName));
        }

        [Test]
        public void ShouldFindTheCorrectSecondWord()
        {
            Assert.AreEqual("second", fileInspector.SecondWord);
        }
    }

We've added two new delegates and moved the code from Arrange and Act accordingly and we execute those delegates within our arrange and our act. It's now time to do the same for the tests:

    [TestFixture]
    public class FileInspectorTest_WhenInspectingAFileFromAFileName : ContextTest
    {
        static IFileInspector fileInspector;
        static IFileReader fileReader;
        static string fileName;
        static string fakeContent;

        Action arrange = () =>
         {
             fileName = "filename";
             fakeContent = "fake content for the test";
             fileReader = MockRepository.GenerateStub<IFileReader>();
             fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);
             fileInspector = new FileInspector(fileReader);
         };

        Action act = () =>
         {
             fileInspector.InspectFile("filename");
         };

        protected override void Arrange()
        {
            arrange();
        }

        protected override void Act()
        {
            act();
        }

        Action shouldTellTheFileReaderToObtainTheContentFromTheFile = () => 
            fileReader.AssertWasCalled(x => x.GetContentFromFile(fileName));

        Action shouldFindTheCorrectSecondWord = () =>
            Assert.AreEqual("second", fileInspector.SecondWord);

        [Test]
        public void ShouldTellTheFileReaderToObtainTheContentFromTheFile()
        {
            shouldTellTheFileReaderToObtainTheContentFromTheFile();   
        }

        [Test]
        public void ShouldFindTheCorrectSecondWord()
        {
            shouldFindTheCorrectSecondWord();
        }
    }

Now we control all of our tests through delegates, but we have to explicitly call them with our AAA context test structure.

What if the testing framework could call these delegates on our behalf? How would it be able to keep the AAA syntax and ensure they are called in the correct order?

If we create some strong delegates of our own, we can effectively give each delegate a type and this can then be examined through reflection:

    [TestFixture]
    public class FileInspectorTest_WhenInspectingAFileFromAFileName : ContextTest
    {
        static IFileInspector fileInspector;
        static IFileReader fileReader;
        static string fileName;
        static string fakeContent;

        public delegate void Arrange();

        public delegate void Act();

        public delegate void Assertion();

        Arrange arrange = () =>
         {
             fileName = "filename";
             fakeContent = "fake content for the test";
             fileReader = MockRepository.GenerateStub<IFileReader>();
             fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);
             fileInspector = new FileInspector(fileReader);
         };

        Act act = () =>
         {
             fileInspector.InspectFile("filename");
         };

        Assertion shouldTellTheFileReaderToObtainTheContentFromTheFile = () => 
            fileReader.AssertWasCalled(x => x.GetContentFromFile(fileName));

        Assertion shouldFindTheCorrectSecondWord = () =>
            Assert.AreEqual("second", fileInspector.SecondWord);
    }

Our test now looks very different and we effectively have the same pattern as Machine.Specifications or other delegate driven testing frameworks. It would be very easy to reflect on the type and pull out each delegate at runtime which these frameworks do in some manner such as:

        var arranges = typeof(FileInspectorTest_WhenInspectingAFileFromAFileName)
            .GetFields(BindingFlags.NonPublic)
            .Where(x => x.FieldType == typeof(Arrange));

        var acts = typeof(FileInspectorTest_WhenInspectingAFileFromAFileName)
         .GetFields(BindingFlags.NonPublic)
         .Where(x => x.FieldType == typeof(Act));

        var assertions = typeof(FileInspectorTest_WhenInspectingAFileFromAFileName)
         .GetFields(BindingFlags.NonPublic)
         .Where(x => x.FieldType == typeof(Assertion));
  • Step Five : make the transition to Machine.Specifications and embrace the full contextual syntax and descriptive style
    [Subject(typeof(FileInspector))]
    public class when_inspecting_a_file_from_a_file_name
    {
        Establish context = () =>
        {
            fileName = "filename";
            fakeContent = "fake content for the test";
            fileReader = MockRepository.GenerateStub<IFileReader>();
            fileReader.Stub(x => x.GetContentFromFile(fileName)).Return(fakeContent);
            fileInspector = new FileInspector(fileReader);
        };

        Because of = () => fileInspector.InspectFile("filename");

        It should_tell_the_file_reader_to_obtain_the_content_from_the_file = () =>
            fileReader.AssertWasCalled(x => x.GetContentFromFile(fileName));

        It should_find_the_correct_second_word = () =>
            Assert.AreEqual("second", fileInspector.SecondWord);

        static string fileName;
        static string fakeContent;
        static IFileReader fileReader;
        static FileInspector fileInspector;
    }



0 comments: