Learn More





DNN Community Blog

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

Creating Testable Modules - Adding Tests

This article is cross-posted from my personal blog.

In the previous article in this blog series on creating testable modules, I created a simple MVP based application.  In this article I  will add a Test project to the solution and some tests.  Figure 1 shows the Test project as it looks in Visual Studio’s Solution Explorer. 

Figure 1 – The Test Project

In this example project there are three classes, the Test class itself (HellowWorldMVPTests.cs) a Mock class (MockView.cs) and a utility class (Constants.cs).

There are many testing frameworks we could use.  For convenience, I will be using MSTest, which is the default Unit Test Framework in Visual Studio.  When you create a new Test project in Visual Studio, a single class file is added which is already set up ready for writing tests. In  MSTest a test class is identified by the class-level attribute “TestClass” (see Listing 1).

Listing 1 – The Test Class before adding tests

   1:  [TestClass]
   2:  public class HeloWorldMVPTests
   3:  {
   5:  }

Writing our First Test

So now we have a Test Project, lets write the first test.  If we go back to our specifications in the previous article, we will see that one of the specifications was “On initially browsing to the page the label will give the user instructions”, so lets start by writing a test to simulate that specification.

The tests I will be writing are designed to demonstrate that the Presenter is correctly setting the properties of the View.  As the real View is a User Control it is not easily instantiated outside of a WebForm, so first, I will create a Mock View class to simulate the behavior of a View and allow us to test the Presenter (see Listing 1).

Listing 1 - the MockView class

   1:  class MockView : IView
   2:  {
   3:      private string text;
   4:      private string label;
   6:      public MockView()
   7:      {
   8:      }
  10:      public MockView(string text)
  11:      {
  12:          this.text = text;
  13:      }
  15:      public string GetLabelContent()
  16:      {
  17:          return label;
  18:      }
  20:      public string Label
  21:      {
  22:          set { label = value; }
  23:      }
  25:      public string Text
  26:      {
  27:          get { return text; }
  28:      }
  29:  }

The MockView class implements the same IView interface as the HelloWorldView class - a write-only string property - Label, and a read-only string property - Text.  The MockView has two constructors - the reason for this will become apparent later, and a helper method that returns the value of the private member label - this method enables our test class to determine if the value of the label was set properly, as the Label property is write-only.

Our initial test is shown in Listing 2.  Note that as with the class, the method is decorated with an attribute - "TestMethod" - which tells the Visual Studio Unit Test Framework that this method is a Test.

Listing 2 – The first Test

   1:  [TestMethod]
   2:  public void View_Should_Display_IntroText_When_First_Loaded()
   3:  {
   4:      MockView view = new MockView();
   5:      Presenter presenter = new Presenter(view);
   7:      //Call the presenters OnViewLoaded method to simulate the 
   8:      //Page Load (isPostBack = false)
   9:      presenter.OnViewLoaded(false);
  11:      //Assert that the View displays the IntroText
  12:      Assert.AreEqual(Constants.IntroText, view.GetLabelContent());
  13:  }

I have named the test method View_Should_Display_IntroText_When_First_Loaded.  This is purely a convention, but this name makes it clear what passed and what failed when the test results are listed.  This test is designed to verify that the View will display the introductory text when the page is first loaded.

In line 4 of the test the MockView is created.  As the default constructor is used, the View's text property starts off empty, as the HelloWorldView user control does when it is first displayed.  Line 5 creates a Presenter, passing the  MockView as a parameter of its constructor.  Remember the Presenter’s constructor takes an IView, so any concrete implementation is accepatable. 

On line 9 we call the Presenter's OnViewLoaded method, which simulates the View being loaded into the WebForm.

Finally on line 12 we get the guts of the test - we use the Test Framework's Assert class to determine if the View's label is correctly set to the introductory text which is defined in the Constants class (see Listing 3).

Listing 3 - The Constants class

   1:  class Constants
   2:  {
   3:      internal const string IntroText =
   4:          "Enter your name and click the Submit Button";
   5:      internal const string HelloText =
   6:          "World";
   7:  }

So that's the first test.  If the project is compiled and the test run we should see one green check-mark.

Figure 2 - The results of the First Test

Yes - our test passed, now we can celebrate!

The Remaining Tests

Its always great to see those check marks, but this is just one test.  Figure 3 shows the code coverage results for this test.

Figure 3 - Code Coverage for the First Test

This one test covers a little less than 60% of the code in the Presenter class.  In order to cover 100% of the Presenter class we need to add some more tests. 

Two possible tests stand out from the initial specifications - "When the user clicks the Submit button, the label is updated to display ‘Hello ‘ plus the text the user enters in the text box" and "When the user clicks the Reset button, the label is reset to display the initial instructions". 

The tests for these two specifications are shown implemented in two new test methods (see Listing 4)

Listing 4 - The Remaining Tests

   1:  [TestMethod]
   2:  public void View_Should_Display_HelloText_When_Submit_Button_Is_Clicked()
   3:  {
   4:      MockView view = new MockView(Constants.HelloText);
   5:      Presenter presenter = new Presenter(view);
   7:      //Call presenter's Update method to simulate the Submit button
   8:      presenter.Update();
  10:      //Assert that the View displays the HelloText
  11:      Assert.AreEqual("Hello - " + Constants.HelloText, view.GetLabelContent());
  12:  }
  14:  [TestMethod]
  15:  public void View_Should_Display_IntroText_When_Reset_Button_Is_Clicked()
  16:  {
  17:      MockView view = new MockView(Constants.HelloText);
  18:      Presenter presenter = new Presenter(view);
  20:      //Call presenter's Reset method to simulate the Reset button
  21:      presenter.Reset();
  23:      //Assert that the View displays the IntroText
  24:      Assert.AreEqual(Constants.IntroText, view.GetLabelContent());
  25:  }

The first test View_Should_Display_HelloText_When_Submit_Button_Is_Clicked verifies that when the submit button is clicked the label will be updated to display "Hello " plus whatever the user entered in the text box.  The second test verifies that when the reset button is clicked the label displays the introductory text, even if there is text in the text box - View_Should_Display_IntroText_When_Reset_Button_Is_Clicked

These two tests demonstrate the use of the extra overload on the MockView.  As the IView interface only exposes the Text property as read-only, we have no mechanism to set the value - to simulate the user entering text.  We therefore use the constructor overload to set the value.

Figures 4 and 5 show the test results and code coverage for the test project with all three tests.  All three tests passed and we now have 100% code coverage for our Presenter class.  This gives us a high degree of confidence that the code is correct.

Figure 4 - Test Results for all Three Tests

Figure 5 - Code Coverage for all Three Tests

Note: We do not have 100% code coverage of the whole HelloWorldMVP project, but the difference with this approach, compared with the more conventional WebForm design, is that most of our logic has been removed from the UserControl’s code behind and placed in a separate class – the Presenter – which we can test with 100% code coverage.  Our UserControl – the View – now consists of simple properties and event handlers.

This is just a simple example - but I hope it demonstrates most of the principles we will use as we build our Testable DotNetNuke Module.


Comment Form

Only registered users may post comments.


2sic Daniel Mettler (124)
Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (21)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (203)
Chris Paterra (55)
Clinton Patterson (28)
Cuong Dang (21)
Daniel Bartholomew (2)
Dave Buckner (2)
David Poindexter (3)
David Rodriguez (2)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (6)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (269)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (52)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (37)
Will Strohl (163)
William Severance (5)
Try Evoq
For Free
Start Free Trial
a Demo
See Evoq Live
Need More Information?