Jeffrey Palermo (.com)

Sponsors

The Lounge

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Unit testing demonstrated – level 300

I use NUnit for my automated tests.  Because of that, all my tests are unit tests, right?

WRONG!  The name of the testing framework has no bearing on the type of test you have.  NUnit is a framework for running automated tests.  You _can_ write unit tests with it, but you can also write integration tests as well as full-system tests with it.  A unit test is a special type of developer test and can be done with or without NUnit. 

 

A unit test tests a single line of code.

How big is a unit?  Well, that’s up to you, and there is not scientific answer.  Typically, you only give a class a single responsibility.  The class may have several methods since the class may need to do several things to accomplish the single responsibility.  The class may have to collaborate with several other classes to accomplish its purpose.  A unit of code is an identifiable chunk of code needed to accomplish part of a responsibility.  Is that vague enough for you?  In my example below, I’ll clear this up a bit.

 

A unit test isolates the code being tested.

A class will need to talk to other classes.  That’s a given.  Sometimes this is ok for testing, and sometimes this just gets in the way.  It might be ok to talk to a class that just builds a string (like StringBuilder), but it’s not ok to talk to a class that grabs information from a configuration file.  In a unit test, you need to take environmental dependencies out of the equation so that a pass or failure is truly dependent on the code being tested.  You don’t want the test failing because the configuration file wasn’t in the right spot.  There are plenty of techniques available for this.  To start, you need to code against interfaces and use fake objects like stubs and mocks.  I like the Rhino mock framework for this.

 

Here are some dependencies that will need to be frequently simulated for unit testing:

  • Config files
  • Registry values
  • Databases
  • Environment variables
  • Machine name
  • System clock (Yup.  Even that has the potential to get in your way).

 

Example:

This example will show a real web user control that I’ve unit-tested.  This is not theoretical.  This is inside my EZWeb software.  The purpose of the following screen is to maintain a few pieces of information for the page being viewed.  The user can set the title of the page and some other things.

I’ve used the Mode-View-Presenter pattern to make unit testing this easier.  Obviously if all my code is in the code-behind of the ASCX, then I won’t be able to test any of it because I can’t run that code outside of the ASP.NET runtime.  If you aren’t familiar with the MVP pattern now, take some time to read up on it.  The presenter is the controlling class that will be tested.  The code-behind becomes very dumb.  The code-behind will implement my view interface and be responsible for taking information and setting the correct control.  The view is very small, and all the intelligence is in the controlling class (the Presenter).  The presenter is where the bugs will hide, so I’ll unit test that class.  The model is represented by an interface IPageConfig that you’ll see being referenced.

 

The following example shows a unit test of the code that gets data from the model and publishes it to the view.  The textboxes and drop-downs need to be set properly.  This is not the full code.  The full code also reacts to the save button being clicked and taking modifying information and saving it.

 

Here is the view interface:

namespace Palermo.EZWeb.UI

{

    public interface IPagePropertiesView

    {

        string Title { get; set; }

        bool HasParent { get; set; }

        string Template { get; set; }

        string Theme { get; set; }

        string Plugin { get; set; }

        string Parameter { get; set; }

        bool IsPostback { get; }

        DictionaryList GetTemplateChoices();

        void SetTemplatesDropDown(DictionaryList list);

        DictionaryList GetThemeChoices();

        void SetThemesDropDown(DictionaryList list);

        DictionaryList GetPluginChoices();

        void SetPluginDropDown(DictionaryList list);

        void EnableTitle(bool enabled);

        void EnableTemplate(bool enabled);

        void EnableTheme(bool enabled);

        void EnablePlugin(bool enabled);

        void EnableParameter(bool enabled);

        void ReloadParent();

    }

}

 

Here is the code-behind that implements the view interface (truncated):

      public partial class PageAdministration : UserControl, IPlugin, IPagePropertiesView

      {

        private PagePropertiesPresenter _presenter;

 

        public string Title

        {

            get { return txtTitle.Text; }

            set { txtTitle.Text = value; }

        }

 

        public bool HasParent

        {

            get { return Convert.ToBoolean(ddlHasParent.SelectedValue); }

            set { ddlHasParent.SelectedValue = value.ToString(); }

        }

 

        public string Template

        {

            get { return ddlTemplate.SelectedValue; }

            set { ddlTemplate.SelectedValue = value; }

}

. . .

 

Here is the part of the Presenter that we’ll be focusing on:

    public class PagePropertiesPresenter

    {

        private readonly IPagePropertiesView _view;

        private ICurrentContext _context;

 

        public PagePropertiesPresenter(IPagePropertiesView view)

        {

            _view = view;

            _context = (ICurrentContext) ObjectFactory.GetInstance(typeof (ICurrentContext));

        }

 

        //testing constructor

        public PagePropertiesPresenter(IPagePropertiesView view, ICurrentContext context)

        {

            _view = view;

            _context = context;

        }

 

        public virtual void LoadConfiguration()

        {

            if (!_view.IsPostback)

            {

                IPageConfig config = _context.GetPageConfig();

                _view.Title = config.Title;

                _view.HasParent = config.HasParent;

 

                DictionaryList templates = removeBadItems(_view.GetTemplateChoices());

                _view.SetTemplatesDropDown(templates);

                _view.Template = config.Template;

 

                DictionaryList themes = removeBadItems(_view.GetThemeChoices());

                _view.SetThemesDropDown(themes);

                _view.Theme = config.Theme;

 

                DictionaryList plugins = removeBadItems(_view.GetPluginChoices());

                _view.SetPluginDropDown(plugins);

                _view.Plugin = config.Plugin;

 

                _view.Parameter = config.Parameter;

            }

}

.  .  .

 

Notice that the LoadConfiguration() method checks for postback (through the view) and then uses the MODEL to set pieces of information on the VIEW.  You may think that this code is boring, but it’s essential for the behavior of the screen.

 

Now for the test.  Note that we’re simulating the view and the ICurrentContext interface since these are collaborators.  The ICurrentContext provides the MODEL to the Presenter:

    [TestFixture]

    public class PagePropertiesPresenterTester

    {

        [Test]

        public void ShouldSetAllInformationOnPage()

        {

            MockRepository mocks = new MockRepository();

            IPageConfig mockConfig = (IPageConfig)mocks.CreateMock(typeof(IPageConfig));

            IPagePropertiesView view = (IPagePropertiesView)mocks.CreateMock(typeof(IPagePropertiesView));

            ICurrentContext context = (ICurrentContext)mocks.CreateMock(typeof(ICurrentContext));

            Expect.Call(view.IsPostback).Return(false);

            Expect.Call(context.GetPageConfig()).Return(mockConfig);

            string title = "fake title";

            Expect.Call(mockConfig.Title).Return(title);

            view.Title = title;

 

            bool hasParent = false;

            Expect.Call(mockConfig.HasParent).Return(hasParent);

            view.HasParent = hasParent;

 

            string selectedItem = "foo";

 

            Expect.Call(mockConfig.Template).Return(selectedItem);

            DictionaryList list = new DictionaryList();

            list.Add("first", "first");

            list.Add("Foo", selectedItem);

            list.Add(".svn", ".svn");

            list.Add("_something", "_something");

 

            Expect.Call(view.GetTemplateChoices()).Return(list);

            view.SetTemplatesDropDown(null);

            LastCall.On(view).Constraints(new PropertyIs("Count", 2));//. and _ should be stripped out.

            view.Template = selectedItem;

 

            Expect.Call(mockConfig.Theme).Return(selectedItem);

            Expect.Call(view.GetThemeChoices()).Return(list);

            view.SetThemesDropDown(null);

            LastCall.On(view).Constraints(new PropertyIs("Count", 2));

            view.Theme = selectedItem;

 

            Expect.Call(mockConfig.Plugin).Return(selectedItem);

            Expect.Call(view.GetPluginChoices()).Return(list);

            view.SetPluginDropDown(null);

            LastCall.On(view).Constraints(new PropertyIs("Count", 2));

            view.Plugin = selectedItem;

 

            string fakeParameter = "faky";

            Expect.Call(mockConfig.Parameter).Return(fakeParameter);

            view.Parameter = fakeParameter;

 

            mocks.ReplayAll();

 

            PagePropertiesPresenter presenter = new PagePropertiesPresenter(view, context);

            presenter.LoadConfiguration();

 

            mocks.VerifyAll();

}

. . .

 

Your first thought might be that this unit test method is too long.  It certainly pushes my comfort level as well.  I could have chosen a small one for this example, but I chose my largest one instead.  I’ve seen other unit testing examples that are so trivial that they don’t demonstrate much.  In this sample, I chose one of the most difficult things to unit test:  A UI screen.  Notice that I’m using Rhino mocks to set up my fake objects.  The call to “mocks.VerifyAll()” does a check to ensure that the collaborators were called with the correct input.  After all, my presenter method is in charge of getting information from the MODEL and publishing them to the VIEW.  If you spend some time going over this test, you can see some of the rules the code has to live by.  One of the side-effects of the unit test is documentation of the code (developer documentation).  At this point, I can refactor my method knowing that I have this test as a safety net.

 

How do I unit test my legacy code?

Change it.  This screen started out several years ago with all the code in the code-behind class.  It was impossible to test this way.  I had to refactor to the MVP pattern to enable testing.  I had to break some things away by inserting an interface so that I’d have a seam to break dependencies.  In short, you must refactor your existing code to get it to a point where it is testable.  The reason it’s not testable is that it’s tightly-coupled with its dependencies.  I hope by now that the words “loosely-coupled” are recognized as “good” and “tightly-coupled” are recognized as “bad”.  Testable code is loosely-coupled.  Loosely-coupled code is testable.  And now the big leap:  Testable code == good.


Posted Sat, May 6 2006 6:25 PM by Jeffrey Palermo

[Advertisement]

Comments

Ayende Rahien wrote re: Unit testing demonstrated – level 300
on Sun, May 7 2006 4:50 AM
The problem with this style of test is that it looks fragile to me.
You are basically repeating the code you are testing in the test using mocks. Nearly any change that you'll make there will break the test.

How do you handle this?
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Sun, May 7 2006 9:39 AM
Ayende,
I'd certainly be open to other ideas about unit testing this Presenter method.  In this craft, I learned quickly that there can be several correct ways to do things.

I'll be honest and say that I don't understand how the test is fragile.  The code needs to get information from model and set it to controls on the veiw.  There isn't any domain logic in this code, just coordinating logic.  There are 6 controls, so there are six pieces of information that need to be set.  The code can be reordered and refactored at will, and this tests will still pass, so I'm not sure I understand what you are telling me.  

JBarbatos wrote re: Unit testing demonstrated – level 300
on Sun, May 7 2006 12:23 PM
I see you have also been using Fitnesse, how would something like this fit into these tests? Or does it at all?

Based of the above sample, you would have 2 projects, EZWeb apps and something like EZWeb.Tests right? Assuming you were to use Fitnesse as well, would you then create a thrid assembly like EZWeb.Fitnesse or just keep all tests together in EZWeb.Tests?

Jason Haley wrote Interesting Finds
on Sun, May 7 2006 12:48 PM
Ayende Rahien wrote re: Unit testing demonstrated – level 300
on Sun, May 7 2006 4:38 PM
The problem is that the test basically create a set of expectations for every line of code.

Any change to LoadConfiguration will break the test. Even something as simple as adding logging information, which I usually don't have tests for.

I don't see another way to test it, to tell you the truth, but I have problems with the test being so very tightly coupled with the code.
Vikas Kerni wrote re: Unit testing demonstrated – level 300
on Sun, May 7 2006 5:46 PM
Very nice post.
You forced me to read Mock Objects.
After reading your post , I have to read article Introducing Rhino Mocks by Ayende.
Nice Article, Ayende. Then again I read your post and it made sense to me. I am very sure that this framework will be very helpful for  writing  automated unit tests.
I really liked your pragmatic remarks at Ar-agilist post discussion.
I am looking forward to your Article demonstrating how you write automated test cases for entity classes which perform CRUD operations.
Liang wrote re: Unit testing demonstrated – level 300
on Mon, May 8 2006 9:17 AM
Hi Jeffrey,

It is a very nice post. I am just curious if you apply TDD, which means you use Mock or any other unit testing framework in your design process? or using as pure testing? If you follow TDD, you still have a basic application architecture in your mind when writing the code?

Thanks!
Crad's .NET Blog wrote re: Barra rossa, scrivo codice, barra verde
on Mon, May 8 2006 3:05 PM
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Mon, May 8 2006 3:58 PM
JBarbatos,
Yes, I also do FIT tests.  FitNesse is a GUI for running FIT tests.  I start with unit testing to test the smallest units of code first.  I then combine integration tests in the small, and then for the full system.  FIT tests then  run the same way as full-system integration tests, but they are readable by non-technical folks.  

I do break my tests into another assembly.  My application assembly is EZWeb.dll, and I have EZWeb.UnitTests.dll and EZWeb.IntegrationTests.dll.  I have a Fit namespace inside integration tests for my FIT fixtures.  It's up to you how to split them out, though.  I wouldn't say that there is any rule on how to do it.  You don't, however, want to deploy these tests with the application normally.
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Mon, May 8 2006 4:00 PM
Ayende,
I'm still open for suggestions, but I've found that all my unit tests are very specific to the code.  After all, they exist to validate that unit of code.  I can reorder and extract methods all day long, but as long as I'm talking to the dependencies in the same way, this test will pass.  UI code is very different from other code.  UI code is boring (ask something for this data - push this data to UI controls).  Other parts of the system get more interesting.
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Mon, May 8 2006 4:02 PM
Vikas,
I may write a post on unit testing data access controllers in the future.  I can say right now that I'll use NHibernate, but the data access controllers will be tested in isolation first before being tested along with the database.

Interfaces are the key. . . and a service locator tool like StructureMap or Spring.
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Mon, May 8 2006 4:06 PM
Liang,
I do apply TDD to modifying code normally, but when refactoring legacy code to be more testable, I find it's hard to write a test first.  I tend to break the hard dependencies first and then write a unit test.  At the point that I've made the old code loosely-coupled, I can then write more tests first.  I value unit testing whether the test is written first or second.

I use Rhino mocks exclusively as my mock framework.  From time to time, I'll use concrete mock and stub classes, though.

I always have an application architecture in mind when writing code.  The application just won't fall into place.  Without a design, what can you make?  Now, certain conditions pop out midstream as well, so I find that I might have to shift directions a bit while I'm going.  You can never design and predict everything from the start.
Liang wrote re: Unit testing demonstrated – level 300
on Mon, May 8 2006 7:35 PM
Jeffrey,

Thank you so much for your reply! I am using Rhino Mock as well. Thanks Ayende! That is the best I can find so far. But I still have some confusion regarding the process of TDD. When I read the TDD articles, it seem to ask me to write method by method first. Then through continuous refactoring, we can reach a better design. So that is my question coming from. Obviously we can not expect everything from start, but as you said, we DO have a basic architecture in mind, for example, I have to decide to follow a real Domian/Repository pattern or other type of mapping patterns. Put another way, TDD itself won't lead us to that.

Thanks again!
Joshua Flanagan wrote re: Unit testing demonstrated – level 300
on Tue, May 9 2006 9:46 AM
I was making Ayende's point at a recent AgileATX lunch as I was writing the same kidns of tests, but feeling uncomfortable about it. Unit tests that use a lot of mocking to re-iterate each step in the class being tested do feel fragile. Your unit test is now very tightly coupled to the implementation details. You usually try to hide implementation details from your unit tests (which is why you don't test private methods), so that you can easily refactor the implementation without breaking your tests. Otherwise every time you change your code, you need to change your test.
Jeremy helped me feel a little better about it by saying that (paraphrasing) in this case, you are not testing the interface of the method (inputs & outputs) - you are testing and verifying the interaction of the class with its collaborators. A valid approach, you just have to live with the fragile feeling.
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Tue, May 9 2006 10:47 AM
Josh,
Yes, there is a lot of setup, but this method seems to be working well.  Of course, I'm always open to better ways to do things.

Joshua Flanagan wrote re: Unit testing demonstrated – level 300
on Tue, May 9 2006 4:40 PM
After thinking about it a little more... I wonder if maybe this method doesn't need to be unit tested? Like a simple get/set accessor - the method is not doing any logic (there is only a single conditional (IsPostBack), which is negligible from a testing perspective). If you think of it from the angle of "unit tests as documentation", what additional documentation does this test add? To me, it doesnt add anything. It just regurgitates the code under test. Its like the old guideline about code comments: explain what the code does, not how the code does it. This sort of heavily mocked unit test does not document what the code does, it only documents how it does it.
So now I'm having a crisis of faith and starting to question the value of the MVP pattern for unit testing, since the only part we are (presumably) testing is the Presenter, and it is mainly performing straigtforward "getter/setter" type glue methods with negligible logic.
I'm not criticizing your approach or saying I know a better way - I'm just trying to open the conversation. Are we getting the value that we expect from this type of unit test and design? Maybe we'll have to re-open the topic at the next lunch.
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Tue, May 9 2006 5:01 PM
I don't unit test getters and setters because my take on it is that I'm not going to unit test generated code.  All my getters and setters are generated by Resharper.  

Resharper can't generate my method here.  It does something important.  This method has to coordinate 6 pieces of information from a data source to the right places on the view. . . but only if IsPostBack is false.

That doesn't sound like a method that is immune to bugs.  In my experience, the places where bugs lie are the places that I neglect to test.

The cyclomatic complexity of this method is 2 (not even 1 like getters and setters).  This method is sufficiently complicated that I would not think of ignoring it, and if I don't test it with a unit test, then I have to test it in another way, like running the application manually (and that takes much more time).
Joshua Flanagan wrote re: Unit testing demonstrated – level 300
on Tue, May 9 2006 10:07 PM
What if you used DynamicMocks so that you didn't have to setup all the expections (repeating your implementation code)? Your test would just focus on whether the code does SOMETHING when IsPostBack is true, and NOTHING when it is false.

In your example, you would get rid of everything after:
          Expect.Call(context.GetPageConfig()).Return(mockConfig);
and before:
          mocks.ReplayAll();

Any other logic that may be relevant (like stripping out . and _ folders) could be covered by another simple test.
bobwantstdd wrote re: Unit testing demonstrated – level 300
on Mon, May 22 2006 4:57 PM
Hi Jeffrey,

Didn't want you to think I wrote one long comment here and promptly forgot about you.

I spent about a week playing with nunitasp, along with other tasks,  and then of course got the demand "Show some results"

Now that they have a lousy piece of code that "shows something", is not really too testable and stinks, I can get back to trying to figure out how to do this the right way, or at least some approximation thereof :)

Q#1, "read up on MVP"
I found Fowlers article at http://www.martinfowler.com/eaaDev/ModelViewPresenter.html

Got a recommendation of anything a bit more straightforward?  I could use a URL, if you know one, where someone actually takes hello world, maybe adds a button or two, and then changes it into a mvp version.

Barring that, perhaps a recommended reading list from you?  I like your style of writing more than most others, so I'm hoping your suggestions will save me some time.


Q#2  I understand the point of mocks ( well, i think i do ) and stubs ( pretty sure I do )

The thing is, the group I am working in now didn't even implement source control, in any form whatsoever, until I got here.  The one app I work on usually gets out of synch if anyone but me works on it.  ( At least they WANT to use VSS, so there is hope.... )

Much less implement testing or TDD.  They are mostly a bright bunch, just severely time limited due to management requests.

One of the things I like about NOT mocking or stubbing in this work environment is knowing that a failure is environmental.

Other folks may ( unfortunately ) be in similar situations.

OK, the actual Q#2 , could you perhaps post the codebehind of the above sample.  *thinking* I know what it looks like and seeing could be different.  I want to *know* I know what it looks like.  Even a link to a zip or rar containing these 3 or four code files wouold give me a better picture.  I think I can safely infer the contents of the .aspx itself, but include that if it might be useful to determine the names of the controls.  doh, just noticed its a control, well, whatever source files you feel might be useful.

well, that's enough for now.  I need to figure out how to convert some inherited code into something digestible, while adding functionality, to a third party DB app ( written in vb.net I think.  They don't actually have the source or anything silly like that ) that has no documentation, apparently in three days ( or get fired / yelled at / whatever ) while adding in some automated testing to avoid self-foot-shooting.
Oh, and write it all clean enough that the nightmares stop too.
Read ya later...
Jeffrey Palermo wrote re: Unit testing demonstrated – level 300
on Sun, May 28 2006 2:59 PM
Bob,
Thanks for the feedback.  I have a long bloggin todo list, and I'll post a full sample when I'm satisfied that it clearly illustrates the pattern.