One of the biggest challenges in writing Unit Tests - at least when you write them after you have created the actual code, rather than in a Test Driven Development style, is determining what should be tested.

Lets look at a simple example in TermController.

Figure 1: A simple method which needs Unit Testing
   1:  Public Sub AddTermToContent(ByVal term As Term, ByVal contentItem As ContentItem) _
   2:                  Implements ITermController.AddTermToContent
   3:      'Argument Contract
   4:      Requires.NotNull("term", term)
   5:      Requires.NotNull("contentItem", contentItem)
   6:   
   7:      _DataService.AddTermToContent(term, contentItem)
   8:  End Sub

What do we need to test?

  1. Passing a null Term should throw an exception
  2. Passing a null ContentItem should throw an exception
  3. Passing valid (non-null) arguments should cause the AddTermToContent method of the _DataService to be executed

Are there any more – should we test that adding the term to the content item actually results in the association being made, or should we test what happens if this term is already associated with the content item?

The short answer is No – we don’t need to test these cases as that is the responsibility of the DataService, the Controller’s responsibility is to validate the data passed, possibly execute some business logic, and pass control to the data layer, and this is all covered by these three tests.  We would test these scenarios when writing tests for the DataService itself.

Lets look at a more complex method from the same class.

Figure 2: A more complex method which needs Unit Testing
   1:  Public Function AddTerm(ByVal term As Term) As Integer _
   2:                          Implements ITermController.AddTerm
   3:      'Argument Contract
   4:      Requires.NotNull("term", term)
   5:      Requires.PropertyNotNegative("term", "VocabularyId", term.VocabularyId)
   6:      Requires.PropertyNotNullOrEmpty("term", "Name", term.Name)
   7:   
   8:      If (term.IsHeirarchical) Then
   9:          term.TermId = _DataService.AddHeirarchicalTerm(term, _
  10:                                      UserController.GetCurrentUserInfo.UserID)
  11:      Else
  12:          term.TermId = _DataService.AddSimpleTerm(term, _
  13:                                      UserController.GetCurrentUserInfo.UserID)
  14:      End If
  15:   
  16:      'Clear Cache
  17:      DataCache.RemoveCache(String.Format(_CacheKey, term.VocabularyId))
  18:   
  19:      Return term.TermId
  20:  End Function

In this example we have a much longer list of tests.

  1. Passing a null Term should throw an exception
  2. Passing a Term with a negative VocabularyId should throw an exception
  3. Passing a Term with a null or empty name should throw an exception
  4. If a valid simple Term is passed to the method then the AddSimpleTerm method of _DataService should be called
  5. If a valid simple Term is passed to the method then the TermId property should be set
  6. If a valid simple Term is passed to the method then the TermId should be returned
  7. If a valid hierarchical Term is passed to the method then the AddHeirarchcialTerm method of _DataService should be called
  8. If a valid hierarchical Term is passed to the method then the TermId property should be set
  9. If a valid hierarchical Term is passed to the method then the TermId should be returned
  10. If a valid Term is passed to the method then the Term cache should be cleared.

Both of these examples demonstrate that you need to analyze the method and determine what are its responsibilities when you determine what Unit Tests to write for it.


Posted in: Testing  Tags: , , ,

One of the tenets of Unit Testing is that in order to test a piece of code we often need to create some alternate implementation of one or more of the Interfaces involved in a test.  This concept is generally called a Test Double.  However there are many types of Test Double, and there is some degree of confusion about the types of doubles used.

In this installment of “Adventures in Testing” I will attempt to clarify the types of Test Doubles.

Dummies

Dummies are the simplest type of test double – these are objects that are needed to be passed around but they don’t actually do anything so they can have a very simple implementation.  Usually they are required just to fill a parameter list in a method call.

An example would be a test for the following constructor -

Figure 1: A method that can be tested using a Dummy Test Double
   1:  Public Sub New(ByVal createView As ICreateVocabularyView, _
   2:                 ByVal vocabularyController As IVocabularyController, _
   3:                 ByVal scopeTypeController As IScopeTypeController)
   4:      MyBase.New(createView)
   5:      Arg.NotNull("vocabularyController", vocabularyController)
   6:      Arg.NotNull("scopeTypeController", scopeTypeController)
   7:   
   8:      '.....
   9:  End Sub

In this example the constructor takes three parameters.  If we are testing that the constructor throws an Exception if one of the arguments are null, we need to ensure that the other parameters are not null.  So for example our test to ensure that the Constructor throws an Exception if the VocabularyController is null might look like this.

Figure 2: A test using a Dummy Test Double
   1:  public void Constructor_Requires_Non_Null_VocabularyController()
   2:  {
   3:      //Arrange
   4:      DummyCreateVocabularyView view = new DummyCreateVocabularyView();
   5:      DummyScopeTypeController scopeTypeController = new DummyScopeTypeController();
   6:   
   7:      //Act
   8:      CreateVocabularyPresenter presenter = new CreateVocabularyPresenter(view, 
   9:                                                      null, 
  10:                                                      scopeTypeController);
  11:      
  12:      //.....
  13:  }

We don’t care about the implementation of the first (ICreateVocabularyView) or third (IScopeTypeController) interfaces in this test, we just need them to be non-null – all we care about is that the constructor throws if the second parameter is null.

Fakes

The other three types of Test Doubles all have some form of implementation as we are testing some part of the Interface in question.

Fake objects usually have full working implementations, but they use some shortcut, and are not of production quality.  An example of a Fake object would be when testing the business layer of an application, where we create an in-memory Fake DataService class to simulate the data access, and allow us to test the business layer methods, without requiring a database to be set up.

Stubs

Stubs provide canned answers to calls made during a test, and usually don’t respond at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.

Mocks

Mocks are usually dynamically created by a mocking framework – (in DotNetNuke we use Moq as the mocking framework). Mock objects are preprogrammed with expectations which form a specification of the calls they are expected to receive (see Figure 3)

Figure 3; An example of using a Mock Object
   1:  public void VocabularyListPresenter_On_Add_Redirects_To_CreateVocabulary()
   2:  {
   3:      // Arrange
   4:      Mock<IVocabularyController> mockController = new Mock<IVocabularyController>();
   5:      Mock<IVocabularyListView> view = new Mock<IVocabularyListView>();
   6:      view.Setup(v => v.Model).Returns(new VocabularyListModel());
   7:   
   8:      Mock<HttpContextBase> httpContext = new Mock<HttpContextBase>();
   9:      Mock<HttpResponseBase> httpResponse = new Mock<HttpResponseBase>();
  10:      httpContext.Setup(h => h.Response).Returns(httpResponse.Object);
  11:   
  12:      VocabularyListPresenter presenter = new VocabularyListPresenter(view.Object, 
  13:                                                      mockController.Object)
  14:      {
  15:          HttpContext = httpContext.Object,
  16:          ModuleId = Constants.MODULE_ValidId,
  17:          TabId = Constants.TAB_ValidId
  18:      };
  19:   
  20:      // Act (Raise the AddVocabulary Event)
  21:      view.Raise(v => v.AddVocabulary += null, EventArgs.Empty);
  22:   
  23:      // Assert
  24:      httpResponse.Verify(r => r.Redirect(Globals.NavigateURL(Constants.TAB_ValidId,
  25:                                          "CreateVocabulary",
  26:                                          String.Format("mid={0}", Constants.MODULE_ValidId))));
  27:  }

In Figure 3 we are using 4 separate Mock objects, but lets focus on the two mocks created in lines 8 and 9.  In line 8 we create a Mock HttpContextBase using Moq’s Mock class, and in line 9 we create a Mock HttpResponseBase.  Just creating the Mock does nothing more that create in memory an object that can respond to an interface, or (as in this case) an abstract base class.

In line 10 we setup the Mock HttpContextBase, so that when it receives a call to its Response property it will return the object represented by the Mock HttpResponse object.

In line 15 we set HttpContext property of the presenter to this Mock HttpContextBase.

Now we have set up our test so we can test the view’s AdddVocabulary method (see Figure 4), which is designed to redirect to a new page when the AddVocabulary button is clicked.

Figure 4: The AddVocabulary method that Figure 3 is testing
   1:  Public Sub AddVocabulary(ByVal sender As Object, ByVal e As EventArgs)
   2:      Response.Redirect(NavigateURL(TabId, _
   3:                                    "CreateVocabulary", _
   4:                                    String.Format("mid={0}", ModuleId)))
   5:  End Sub

So, in line 21 we raise the AddVocabulary event by calling the Raise method of the Mock view.  This will cause the presenter’s event handler to be triggered and in line 24 we verify that this caused our Mock Response object to have its Redirect method called, with the expected url.

Note: This example is based on the new Model-View-Presenter code which will be included in DotNetNuke 5.3 and provides a new Module Development Framework that provides full testability.

One of the advantages of a Mocking Framework like Moq is that it can be used to replace most types of Test Double.  For example we can write the test using Dummy objects in Figure 2 to use Mocks (Figure 5).

Figure 5: The example in Figure 2 rewritten using Moq instead of Dummies
   1:  public void Constructor_Requires_Non_Null_VocabularyController()
   2:  {
   3:      //Arrange
   4:      Mock<ICreateVocabularyView> view = new Mock<ICreateVocabularyView>();
   5:      Mock<IScopeTypeController> scopeTypeController = new Mock<IScopeTypeController>();
   6:   
   7:      //Act, Assert
   8:      CreateVocabularyPresenter presenter = new CreateVocabularyPresenter(view.Object, 
   9:                                                          v, 
  10:                                                          scopeTypeController.Object);
  11:   
  12:      //..
  13:  }

Posted in: Testing  Tags: , , , , ,

Often, when writing Unit Tests you find yourself writing a batch of quite similar tests that exercise the various test cases for a method.

As I am endeavoring to add Unit Tests to all new code I write, I am learning my way through the MbUnit/Gallio Testing Framework which we have standardized on for all DNN testing.  MbUnit is an awesome testing framework and it has a number of Attributes that you can apply to a Test that allows you to provide different parameters. 

Listing 1 shows an example of using the Row attribute to provide three different sets of test data, rather than writing three separate tests.

Listing 1 – An Example of using MbUNit’s Row Attribute
   1:  [Test]
   2:  [Row("NoRecords", 0)]
   3:  [Row("OneIndividual", 1)]
   4:  [Row("TwoIndividuals", 2)]
   5:  public void ReadIndividual_Returns_The_Correct_No_Of_Records(string fileName, int recordCount)
   6:  {
   7:      //Arrange
   8:      GEDCOMReader reader;
   9:      GEDCOMRecordList records;
  10:   
  11:      //Act
  12:      using (Stream s = Util.GetTestFileStream(String.Format("GEDCOMReaderTests.{0}.ged", fileName)))
  13:      {
  14:          reader = GEDCOMReader.Create(s);
  15:          records = reader.ReadIndividuals();
  16:      }
  17:   
  18:      //Assert
  19:      Assert.AreEqual(recordCount, records.Count);
  20:  }

If you look at the method signature you will notice that there are two parameters, and there are also two parameters to the Row Attribute.  In this case I am writing a parser to read GEDCOM data (GEDCOM is a Genealogical Data Standard). 

The fileName parameter tells the test which GEDCOM file to use (line 12), and the recordCount parameter tells the test the number of records that the file should contain which is used in the Assert (line 19).  In this example (which is quite trivial) I am testing that files with 0, 1 and 2 records parse correctly. 

When the test is executed, all three sets of data are used – so we actually have 3 tests, and we get three results.

As more edge cases are determined – for example in this example we would probably want to test that the correct number of individuals are returned when the file contains other records as well (the GEDCOM standard supports a number of record types – families, notes, sources) – we can add extra tests by just adding Row attributes that correspond to the file names and expected record counts.


Posted in: Testing  Tags: , ,

 Search Blog

 Adsense

 Calendar

«  September 2010  »
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
View posts in large calendar

 Tags

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010 Thoughts from the Wet Coast