Build your own CAB Part #10 – Unit Testing the UI with NUnitForms

I’ve long, long since left the rails of “Build your own CAB” topics and wandered off into “just things you do” when you’re building a WinForms application.  I’m doing this topic now because it felt easy to do in the midst of a hectic week before some time off.  Other topics will follow shortly dependent upon my bouts with writer’s block.

Previously on Soap,

  1. Preamble
  2. The Humble Dialog Box
  3. Supervising Controller
  4. Passive View
  5. Presentation Model
  6. View to Presenter Communication
  7. Answering some questions
  8. What’s the Model?
  9. Assigning Responsibilities in a Model View Presenter Architecture
  10. Domain Centric Validation with the Notification Pattern

Confused?  Well, you won’t be after this week’s episode of Soap.

I’ve frequently read critiques of Test Driven Development that question it’s effectiveness or practicality in regards to user interface code.  It’s certainly more challenging to test drive user interface code, but it’s still very practical.  There is some specific “lore” that TDD practitioners have created over the last couple years to address automated unit testing against user interface code.  The best single answer in my book is to utilize Humble View’s of some sort or another to remove as much code as possible from the clutches of the WinForms presentation infrastructure.  At some point though, you’re left with the actual View code and it’s a nice cozy home for bugs to breed in.  You can take a rationalized approach and say that you’ve reduced the View to code that’s so simple that it’s not worth the time investment to unit test the View’s with automated tests.  Another option that we’ll explore here is to unit test the WinForms View classes with the  NUnitForms library.

NUnitForms

I’ve been meaning to write a post on using NUnitForms for a long time because it’s a great tool that’s never gotten the attention that I think it deserves.  NUnitForms is a library that can be used from within any of the xUnit tools (NUnit/MbUnit/MSTest) to enable unit testing of View behavior.  NUnitForms can drive a screen by finding and manipulating the controls on a screen, as well as check properties of controls at runtime.  For the most part I only use NUnitForms to write very simple unit tests on the wiring from View to Presenter, but NUnitForms can go much farther than that.  In an earlier post I decried the usage of the Autonomous View as a generally untestable and unmaintainable mess.  While I think that’s definitely the case for a screen of any nontrivial complexity, we can happily unit happily forgo View/Presenter separation for smaller screens without sacrificing unit test coverage.  Let’s take one last look at the sample Shipping screen from the Supervising Controller post and see how we could test that screen with NUnitForms if we had taken an autonomous view approach.

Here’s part of the code for that screen:

 

    public partial class ShippingScreen : Form

    {

        private IShippingService _service;

        private Shipment _shipment;

 

 

        public ShippingScreen()

        {

            InitializeComponent();

 

            // Grab an instance of IShippingService from StructureMap

            _service = ObjectFactory.GetInstance<IShippingService>();

 

            // set the Shipment property and start up the data binding

            Shipment = new Shipment();

 

 

            shippingVendorField.SelectedIndexChanged += new System.EventHandler(shippingVendorField_SelectedIndexChanged);

            shippingOptionField.SelectedIndexChanged += new System.EventHandler(shippingOptionField_SelectedIndexChanged);

        }

 

        // Reset the checkbox enabled states anytime a different Shipping Option is selected

        void shippingOptionField_SelectedIndexChanged(object sender, System.EventArgs e)

        {

            resetDeliveryOptions();

        }

 

        // When a new vendor is selected, fill the Shipping Option ComboBox

        // and reset the Delivery Option checkbox’s

        void shippingVendorField_SelectedIndexChanged(object sender, System.EventArgs e)

        {

            // When the Vendor is selected, cascade the list for Shipping Options

            string[] options = _service.GetShippingOptions(_shipment);

            shippingOptionField.Items.Clear();

            shippingOptionField.Items.AddRange(options);

            shippingOptionField.SelectedIndex = 0;

 

            resetDeliveryOptions();

        }

 

        private void resetDeliveryOptions()

        {

            DeliveryOptions options = _service.GetDeliveryOptions(_shipment);

            purchaseInsuranceField.Enabled = options.PurchaseInsuranceEnabled;

            requireSignatureField.Enabled = options.RequireSignatureEnabled;

        }

 

 

        public Shipment Shipment

        {

            set

            {

                // start up the data binding stuff

                _shipment = value;

            }

        }

    }

Easy enough.  Let’s start the unit test harness.  We’ll create our normal NUnit TestFixture class, but this time we’ll inherit from NUnitForm’s NUnitFormTest class to utilize some of it’s bookkeeping code.

 

    // You don’t have to inherit from NUnitFormTest, but it adds some

    // bookkeeping functionality that you may want

    [TestFixture]

    public class ShippingScreenTester : NUnitFormTest

    {

        private ShippingScreen _screen;

 

        private ComboBoxTester stateComboBox;

        private ComboBoxTester shippingVendorComboBox;

        private CheckBoxTester purchaseInsuranceCheckBox;

        private CheckBoxTester requireSignatureCheckBox;

        private TextBoxTester costTextBox;

        private ComboBoxTester shippingOptionComboBox;

 

        private MockRepository _mocks;

        private IShippingService _service;

 

        // Override Setup()

        public override void Setup()

        {

            // I’m using a mock object for the ShippingService here

            // I could just as easily have used a stub, but I can’t stand

            // having those cluttering up the code.

            // It’s somewhat personal preference

            _mocks = new MockRepository();

            _service = _mocks.CreateMock<IShippingService>();

            ObjectFactory.InjectStub(_service);

 

            _screen = new ShippingScreen();

            _screen.Show();

 

            // Create a ControlTester for each field on the screen for later

            stateComboBox = new ComboBoxTester(“stateField”);

            shippingVendorComboBox = new ComboBoxTester(“shippingVendorField”);

            purchaseInsuranceCheckBox = new CheckBoxTester(“purchaseInsuranceField”);

            requireSignatureCheckBox = new CheckBoxTester(“requireSignatureField”);

            costTextBox = new TextBoxTester(“costField”);

            shippingVendorComboBox = new ComboBoxTester(“shippingOptionField”);

        }

 

        // VERY IMPORTANT!  CLOSE THE SCREEN WHEN YOU’RE DONE!

        public override void TearDown()

        {

            _screen.Dispose();

        }

    }

Take a long look at the Setup() method because there’s a lot going on.  The first 3 lines are just setting up the RhinoMocks for IShippingService and directing StructureMap to return the mock object whenever an instance of IShippingService is requested (that’s what the ObjectFactory.InjectStub<T>(T value) method is doing).  The next two lines just create a new instance of the ShippingScreen form and make it visible.  That’s all you need to do.  NUnitForms will happily latch onto the visible form later.

The next thing is to set up “ControlTester” objects for each of the UI controls on the screen.  Each ControlTester is responsible for finding the named control and provides convenience methods to both manipulate a control and check it’s properties.  There are prebuilt ControlTester classes for all of the most common controls plus a generic ControlTester that can be used for everything else.  You can also use a GenericTester like this one:

 

        [Test]

        public void EnableAndDisableCheckboxsWhenTheShippingOptionIsChanged()

        {

            // First, document some preconditions to prove that the code actually did something

            Assert.IsFalse(requireSignatureCheckBox.Properties.Enabled);

            Assert.IsFalse(purchaseInsuranceCheckBox.Properties.Enabled);

 

            string theShippingOptionSelected = “Next Day”// I think I picked up the habit of naming test data

                                            // variables “theSomething” from Colin Kershaw, but

                                            // I don’t completely remember.

 

            DeliveryOptions theDeliveryOptionsReturnedFromTheService = new DeliveryOptions();

            theDeliveryOptionsReturnedFromTheService.PurchaseInsuranceEnabled = true;

            theDeliveryOptionsReturnedFromTheService.RequireSignatureEnabled = true;

 

            // Look at the Constraint.  Little RhinoMocks trick to verify that the “ShippingOption”

            // property on the internal Shipment object has been set prior to making the call to

            // IShippingService.GetDeliveryOptions(Shipment)

            Expect.Call(_service.GetDeliveryOptions(null))

                .Return(theDeliveryOptionsReturnedFromTheService)

                .Constraints(new PropertyIs(“ShippingOption”, theShippingOptionSelected));

 

            _mocks.ReplayAll();

 

            // Select a Shipping Option

            shippingOptionComboBox.Enter(theShippingOptionSelected);

 

            _mocks.VerifyAll();

 

            // Check the enabled property on the two checkbox’s

            Assert.AreEqual(

                theDeliveryOptionsReturnedFromTheService.PurchaseInsuranceEnabled,

                purchaseInsuranceCheckBox.Properties.Enabled);

 

            Assert.AreEqual(

                    theDeliveryOptionsReturnedFromTheService.RequireSignatureEnabled,

                    requireSignatureCheckBox.Properties.Enabled);

        }

Back to our shipping screen.  Here’s the unit test for the cascading dropdown logic when a state or province is selected.  The unit test below does the following:

  1. Set up an expectation on the mock object for IShippingService to return an array of shipping vendor names
  2. Select an option on the dropdown box for “State or Province” (the call to stateComboBox.Enter())
  3. Verify the mock object interaction with the IShippingService
  4. Lastly, check the values on the dropdown box for the shipping vendor

        [Test]

        public void SelectingAStateOrProvinceCascadesTheSelectionListOfVendors()

        {

            // Setup the IShippingService to return a string array of vendors for a given State

            string[] theOptions = new string[]{“Vendor A”, “Vendor B”, “Vendor C”};

            string theStateOrProvince = “TX”;

 

            Expect.Call(_service.GetShippingVendorsForLocation(theStateOrProvince))

                .Return(theOptions)

                .Constraints(new Equal(theStateOrProvince));

 

            _mocks.ReplayAll();

 

            stateComboBox.Enter(theStateOrProvince);

 

            _mocks.VerifyAll();

 

            // Lastly, let’s check the dropdown list on shippingVendor ComboBox

            // The “Properties” property is a reference to the System.Windows.Forms.ComboBox

            // object.  You can write assertions against anything on its public API

            Assert.AreEqual(theOptions, shippingVendorComboBox.Properties.Items);

        }

Now, let’s look at a unit test for enabling/disabling the insurance and signature required checkbox’s when the Shipping Option is selected:

 

        [Test]

        public void EnableAndDisableCheckboxsWhenTheShippingOptionIsChanged()

        {

            // First, document some preconditions to prove that the code actually did something

            Assert.IsFalse(requireSignatureCheckBox.Properties.Enabled);

            Assert.IsFalse(purchaseInsuranceCheckBox.Properties.Enabled);

 

            string theShippingOptionSelected = “Next Day”// I think I picked up the habit of naming test data

                                            // variables “theSomething” from Colin Kershaw, but

                                            // I don’t completely remember.

 

            DeliveryOptions theDeliveryOptionsReturnedFromTheService = new DeliveryOptions();

            theDeliveryOptionsReturnedFromTheService.PurchaseInsuranceEnabled = true;

            theDeliveryOptionsReturnedFromTheService.RequireSignatureEnabled = true;

 

            // Look at the Constraint.  Little RhinoMocks trick to verify that the “ShippingOption”

            // property on the internal Shipment object has been set prior to making the call to

            // IShippingService.GetDeliveryOptions(Shipment)

            Expect.Call(_service.GetDeliveryOptions(null))

                .Return(theDeliveryOptionsReturnedFromTheService)

                .Constraints(new PropertyIs(“ShippingOption”, theShippingOptionSelected));

 

            _mocks.ReplayAll();

 

            // Select a Shipping Option

            shippingOptionComboBox.Enter(theShippingOptionSelected);

 

            _mocks.VerifyAll();

 

            // Check the enabled property on the two checkbox’s

            Assert.AreEqual(

                theDeliveryOptionsReturnedFromTheService.PurchaseInsuranceEnabled,

                purchaseInsuranceCheckBox.Properties.Enabled);

 

            Assert.AreEqual(

                    theDeliveryOptionsReturnedFromTheService.RequireSignatureEnabled,

                    requireSignatureCheckBox.Properties.Enabled);

        }

 

Testing View to Controller Communication

 

Most of our NUnitForms unit tests are just verifying the wiring of a View to a Presenter.  As expected, I’ve gotten a mostly negative reaction to my preference for making the View communicate directly with the Presenter.  One of the commenters questioned whether or not my direct communication would lead to harder testing.  Here’s a sample of testing both direct communication and communication through events.  I don’t think you’re going to see much difference in effort either way.

Here’s the scenario:  when the “Save” button is clicked, call the Save() method on the Presenter.

 

    public interface IPresenter

    {

        void Save();

    }

 

    public class SomeScreen : Form

    {

        private IPresenter _presenter;

        private Button saveButton;

 

        public void AttachPresenter(IPresenter presenter)

        {

            _presenter = presenter;

        }

    }

 

    [TestFixture]

    public class DirectCommunicationTester

    {

        private SomeScreen _screen;

        private MockRepository _mocks;

        private IPresenter _presenter;

 

        [SetUp]

        public void SetUp()

        {

            _screen = new SomeScreen();

 

            // Setup RhinoMocks for the IPresenter and attach it

            _mocks = new MockRepository();

            _presenter = _mocks.CreateMock<IPresenter>();

            _screen.AttachPresenter(_presenter);

 

            _screen.Show();

        }

 

        [TearDown]

        public void TearDown()

        {

            _screen.Dispose();

        }

 

        public delegate void VoidHandler();

 

        // A helper function.  This pays off over the application

        private void assertClickingButtonCalls(string buttonName, VoidHandler handler)

        {

            // Set up the expectation on the Presenter

            handler();

            _mocks.ReplayAll();

 

            // Find the button on the screen

            ButtonTester button = new ButtonTester(buttonName);

 

            // Click the button

            button.Click();

 

            // verify the interaction with the Presenter

            _mocks.VerifyAll();

        }

 

        [Test]

        public void ClickTheSaveButtonCallsSaveOnThePresenter()

        {

            assertClickingButtonCalls(“saveButton”, delegate {_presenter.Save();});

        }

    }

It’s important to note that you generally make all calls to the presenter take in zero arguments.  We’ll see that same pattern repeat for event communication.  You can go farther with the test helper methods to make testing even more declarative. 

Now, the same thing with event driven communication using an anonymous delegate as the event handler:

 

    public delegate void VoidHandler();

 

    public class EventScreen : Form

    {

        private IPresenter _presenter;

        private Button saveButton;

 

        public event VoidHandler OnSave;

    }

 

    [TestFixture]

    public class EventCommunicationTester

    {

        private EventScreen _screen;

 

        [SetUp]

        public void SetUp()

        {

            _screen = new EventScreen();

            _screen.Show();

        }

 

        [TearDown]

        public void TearDown()

        {

            _screen.Dispose();

        }

 

        [Test]

        public void ClickTheSaveButton()

        {

            bool IWasCalled = false;

            _screen.OnSave += delegate { IWasCalled = true; };

            Assert.IsTrue(IWasCalled);

        }

So it’s okay to use Autonomous View after all?

Because of NUnitForms I’m perfectly happy to use the autonomous view style on simple dialog screens, but that’s just about it.  There’s a certain fuzzy point where I’d definitely choose to go to a separated presentation as opposed to an autonomous view.  Even though the shipping screen above is small, I think it already crosses that line.  Many people will term any kind of Model View Presenter (MVP) architecture as more complicated than the autonomous view because of the additional pieces, but I see it differently.  I reject the notion that more classes automatically equals more complexity.  I’d much rather have more classes if that enables me to keep any one single class as simple as possible.  Testability is a huge part of maintainability in my book, but separation of concerns by itself will make the code better organized and easier to understand.

At least in the .Net world the MVP style of construction isn’t being received well by mainstream developers because it’s a foreign approach that isn’t encouraged by the .Net tooling.  In a way MVP is more complex in .Net just because the tooling doesn’t lead you there.  I wouldn’t say that you have to fight the .Net tooling to do separated presentation, but you’ve got to make all of the decisions yourself.I guess it’s easy to understand the negative reaction I see to MVP because it’s not part of the .Net canon yet.  I haven’t taken a hard look at Acropolis yet, but it apparently encourages some sort of View/Presenter separation.  That’s a positive change in direction in .Net tooling that’ll do more good than a thousand blog posts on MVP.

 

Lessons Learned

NUnitForms is definitely meant for unit testing, and that’s where I’ve found it to be most applicable.  On my last project we tried to drive the WinForms application in FitNesse acceptance tests with NUnitForms and struggled mightily.  The complexity of the testing effort goes up dramatically when you move from testing one form or control to the full application at one time.  I’m using StoryTeller/FitnesseDotNet for story testing of the user interface quite successfully on my current project, but that’s a later post.

 

The Future

NUnitForms was written by Luke Maxon with some contributions from Levi Haskell (probably the best developer I’ve ever worked with).  Since Luke’s off living the exciting start up life and coding in Java, I’ve volunteered(?) to be one of the SourceForge administrators for the NUnitForms project now.  I’m thinking of making some additions to NUnitForms for better support of acceptance testing and possibly some prebuilt Fit fixtures, but I’m not sure of either direction or timing.  Since my company does so much WinForms work, I’m going to try to rook some of my colleagues into helping too.  I’m also kicking around the idea of creating a sort of testing DSL with NUnitForms to support something reminiscent of Brian Marick’s wireframe test idea, and possibly use that for acceptance testing as opposed to Fit tests.  I definitely want to add some custom assertions and more options for locating controls.  Got any ideas or requests?  Here’s some loose conceptual code, feel free to let rip with what you think:

 

using (NUnitFormsSpecification specification = new NUnitFormsSpecification){
Click.Button.WithText("Do Something");
Click.Button.Named("saveButton");
CheckBox.WithText("Some flag").Should.Be.Enabled;
Control.Named("bodyPanel").Should.Be.Hidden;
ComboBox.Named("names").Should.Have.Items(new string[]{"Jeremy", "Scott", "Karl", "Jeffrey"});
Select("Jeremy").For.ComboBox.Named("names");

}
 

											

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Build your own CAB, Featured, Test Driven Development. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Amal

    I find it difficult to use NunitForm.
    I have not done any unit Testing before.
    I have a .net windows UI app that I would like to test.
    Please help me through this.
    my email is
    amal_ibrahim_ai@hotmail.com
    Thanks

  • http://www.fluidscape.co.nz/ Duncan Bayne

    > Thanks for the great post. Our shop switched to using WPF
    > exclusively and I’d like to ask, if anybody knows of an
    > alternative to NUnitForms for WPF?

    It’s early days yet, but check out NPresent:

    http://npresent.sourceforge.net/

  • http://www.titaniumsoft.com/ E. Faruk Pehlivanli

    Use white for WPF
    check this out http://www.codeplex.com/white

  • Peter Müller

    Thanks for the great post. Our shop switched to using WPF exclusively and I’d like to ask, if anybody knows of an alternative to NUnitForms for WPF?