Anti-Pattern 5: Fat UI's

One of the major flaws in an un-testable design is the very tight coupling between the UI and the actual domain and/or presentation logic. Our typical ASP.NET applications are difficult to test, because much of the logic is contained within the codebehind files, which derive from Web.UI.Page, which needs an HttpContext, which is difficult to mock. Furthermore, the output of the methods in the codebehind is often not easily-testable, because it's a side-effect (such as calling DataBind() on a GridView).

This same problem exists in windows forms: How do you test the logic inside the form when you are unable to control the input & output of the UI? Let’s examine the following code sample.

   1:  public partial class LoginForm : Form
   2:  {
   3:      const string AUTHNETICATION_FAILED = "Authentication failed!";
   4:      public LoginForm()
   5:      {
   6:          InitializeComponent();
   7:      }
   8:   
   9:      private void LoginButton_Click(object sender, EventArgs e)
  10:      {
  11:          var membershipService = new MembershipService();
  12:          
  13:          if (membershipService.Authenticate(
  14:                                   UserNameText.Text,   
  15:                                   PasswordText.Text
  16:                                   )
  17:                )
  18:          {
  19:              MainForm form = new MainForm();
  20:              this.Hide();
  21:              form.Show();        
  22:          }
  23:          else
  24:          {
  25:              MessageLabel.Text = AUTHNETICATION_FAILED; 
  26:          }
  27:      }
  28:  }

 

Apart from being a pretty naïve implementation of a login form, this code is also hard to test. It’s design contains several smells of un-testable design. First of all it instantiates his Services directly, making it impossible to inject his dependencies through test doubles. Another problem is how can we test that; when the authentication was successful, the MainForm is shown?

By applying what we learned earlier we could already make our SUT more testable:

   1:  public partial class LoginForm : Form
   2:  {
   3:      const string AUTHENTICATION_FAILED = "Authentication failed!";
   4:      private IMembershipService _membershipService;
   5:      private INavigationService _navigationService;
   6:   
   7:      public LoginForm()
   8:      {
   9:          InitializeComponent();
  10:          _membershipService = new MembershipService();
  11:          _navigationService = new NavigationService(this);
  12:      }
  13:   
  14:      public LoginForm(
  15:          IMembershipService membershipService, 
  16:          INavigationService navigationService
  17:          )  //This constructor is only for testing purposes
  18:      {
  19:          _membershipService = membershipService;
  20:          _navigationService = navigationService;
  21:      }
  22:   
  23:      public void LoginButton_Click(object sender, EventArgs e)
  24:      {
  25:          if (_membershipService.Authenticate(
  26:              UserNameText.Text, 
  27:              PasswordText.Text)
  28:             )
  29:          {
  30:              _navigationService.NavigateTo(new MainForm());
  31:          }
  32:          else
  33:          {
  34:              MessageLabel.Text = AUTHENTICATION_FAILED; 
  35:          }
  36:      }
  37:  }
  38:   
  39:  public interface INavigationService
  40:  {
  41:      void NavigateTo(Form focusForm);
  42:  }
  43:   
  44:  public class NavigationService : INavigationService
  45:  {
  46:      private Form _form;
  47:      public NavigationService(Form form)
  48:      {
  49:          _form = form;
  50:      }
  51:   
  52:      public void NavigateTo(Form focusForm)
  53:      {
  54:          _form.Hide();
  55:          focusForm.Show();
  56:      }
  57:  }

 

Here we encapsulated the Navigation logic into a Service and we’ve created a specific constructor that can be used by our tests to inject a test double making possible to write the following test:

   1:  [Test]
   2:  public void LoginButton_Click_OnSuccessfullLogin_ShouldNavigateToMainForm()
   3:  {
   4:      //Arrange
   5:      var mocks = new MockRepository();
   6:      var membershipServiceMock = mocks.StrictMock<IMembershipService>();
   7:      var navigationServiceMock = mocks.StrictMock<INavigationService>();
   8:      var form = new LoginForm(membershipServiceMock,navigationServiceMock);
   9:   
  10:      Expect.Call(membershipServiceMock.Authenticate(null, null)).Return(true);
  11:      navigationServiceMock.NavigateTo(null);
  12:      LastCall.IgnoreArguments();
  13:      mocks.ReplayAll();
  14:   
  15:      //Act
  16:      form.LoginButton_Click(null,null);
  17:   
  18:      //Assert
  19:      mocks.VerifyAll();
  20:  }

 

By applying one of the variants of the MVC pattern namely the PresentationModel we can completely decouple the presentation logic from infrastructure and test it in isolation:

   1:  public interface ILoginFormView
   2:  {
   3:      string UserName { get; set; }
   4:      string Password { get; set; }
   5:      string Message { get; set; }
   6:   
   7:      void NavigateTo(Form focusForm);
   8:  }
   9:   
  10:  public interface IMembershipService
  11:  {
  12:      bool Authenticate(string userName, string password);
  13:  }
  14:   
  15:  public class LoginFormPresenter
  16:  {
  17:      const string AuthneticationFailedMessage = "Authentication failed!";
  18:      private readonly ILoginFormView _view;
  19:      private readonly IMembershipService _membershipService;
  20:   
  21:      public LoginFormPresenter(ILoginFormView view, IMembershipService membershipService)
  22:      {
  23:          _view = view;
  24:          _membershipService = membershipService;
  25:      }
  26:   
  27:      public void LoginButtonClick()
  28:      {
  29:   
  30:          if (_membershipService.Authenticate(
  31:                      _view.UserName,
  32:                      _view.Password
  33:                      )
  34:              )
  35:          {
  36:              _view.NavigateTo(new MainForm());
  37:          }
  38:          else
  39:          {
  40:              _view.Message = AuthneticationFailedMessage;
  41:          }
  42:      }
  43:   
  44:  }
  45:   
  46:  public partial class LoginForm : Form, ILoginFormView 
  47:  {
  48:      private LoginFormPresenter _presenter;
  49:   
  50:      public string UserName
  51:      {
  52:          get
  53:          {
  54:              return UserNameTextBox.Text;
  55:          }
  56:          set
  57:          {
  58:              UserNameTextBox.Text = value;
  59:          }
  60:      }
  61:   
  62:      public string Password
  63:      {
  64:          get
  65:          {
  66:              return PasswordTextBox.Text;
  67:          }
  68:          set
  69:          {
  70:              PasswordTextBox.Text = value;
  71:          }
  72:      }
  73:   
  74:      public string Message
  75:      {
  76:          get
  77:          {
  78:              return MessageLabel.Text;
  79:          }
  80:          set
  81:          {
  82:              MessageLabel.Text = value;
  83:          }
  84:      }
  85:   
  86:      public LoginForm()
  87:      {
  88:          InitializeComponent();
  89:          _presenter = new LoginFormPresenter(this, new MembershipService());
  90:      }
  91:   
  92:      public void LoginButton_Click(object sender, EventArgs e)
  93:      {
  94:         _presenter.LoginButtonClick();
  95:      }
  96:   
  97:      public void NavigateTo(Form focussedForm)
  98:      {
  99:          Hide();
 100:          focussedForm.Show();
 101:      }
 102:  }
 

We are now able to test the presentation logic:

   1:  [TestFixture]
   2:  public class LoginFormPresenterTest
   3:  {
   4:      const string UserName = "myUserName";
   5:      const string Password = "myPassword";
   6:   
   7:      [Test]
   8:      public void LoginButtonClick_AuthenticationSuccess_ViewShouldNavigateToMainForm()
   9:      {
  10:          //Arrange
  11:          var viewMock = CreateLoginFormViewStub();
  12:          var membershipServiceMock = CreateMembershipServiceMock(true);
  13:          var subject = new LoginFormPresenter(viewMock, membershipServiceMock);
  14:   
  15:          //Act
  16:          subject.LoginButtonClick();
  17:   
  18:          //Assert
  19:          viewMock.AssertWasCalled(
  20:              s => s.NavigateTo(null),
  21:              options => options.IgnoreArguments()
  22:              );
  23:      }
  24:   
  25:      [Test]
  26:      public void LoginButtonClick_AuthenticationFailed_ViewMessageIsAuthenticationfailed()
  27:      {
  28:          //Arrange
  29:          var viewMock = CreateLoginFormViewStub();
  30:          var membershipServiceMock = CreateMembershipServiceMock(false);
  31:          var subject = new LoginFormPresenter(
  32:                            viewMock,
  33:                            membershipServiceMock
  34:                            );
  35:   
  36:          //Act
  37:          subject.LoginButtonClick();
  38:   
  39:          //Assert
  40:          Assert.AreEqual("Authentication failed!", viewMock.Message);
  41:      }
  42:   
  43:      private IMembershipService CreateMembershipServiceMock(bool expectedResult)
  44:      {
  45:          var membershipServiceMock = MockRepository.GenerateStub<IMembershipService>();
  46:          membershipServiceMock.Stub(m => m.Authenticate(UserName, Password)).Return(expectedResult);
  47:          return membershipServiceMock;
  48:      }
  49:   
  50:      private ILoginFormView CreateLoginFormViewStub()
  51:      {
  52:          var viewMock = MockRepository.GenerateStub<ILoginFormView>();
  53:          viewMock.UserName = UserName;
  54:          viewMock.Password = Password;
  55:          return viewMock;
  56:      }
  57:  }

 

When designing UI’s you should make your choice between patterns like MVC/MVP/MVVM and use the most appropriate pattern and/or framework for your project.  With the appropriate design a lot of the presentation logic encapsulated into the UI can be extracted and tested. In this example we extracted all the presentation logic encapsulated in our form and transferred it into a presenter. This presenter is completely decoupled from any infrastructure and we are able to easily obtain 100% test coverage with it. The LoginForm itself remains un-testable but it does not contain any logic anymore. His only purpose is to act as a sort of proxy between the windows forms infrastructure and our presentation logic.

 

kick it on DotNetKicks.com

ASP.NET MVC with Webforms

source code can be found here 

It’s now generally admitted in the community that Unit testing and TDD (Test Driven Development) are valuable techniques when it comes to increasing the overall quality of our code. Nevertheless unit testing can be costly especially when you’ve applications with a lot of logic implemented in the UI. Therefore if we want to make our application testable we need to separates the UI from the rest of the application.

Martin Fowler described on his site some patterns that separate and diminish the UI logic to a bare minimum. They are all variants of the classical MVC (Model View Controller) pattern. The MVC split the application in 3 parts: the view handles the display, the controller that responds to user gestures and the model that contains the domain logic. The MVC is the foundation of very popular portal frameworks like Ruby on Rails.

To build web sites applying the MVC pattern with .Net developers can choose among several MVC frameworks like Monorail or the new ASP.NET MVC. In anyway, MVC frameworks like ASP.NET MVC are based on completely different paradigm as the ASP.NET Webforms framework. This means that you have to re-learn to program web apps from scratch. Another setback is that there are no ways to refactor your old ASP.NET applications so that they can fit into the MVC framework. I want to make myself clear, I believe that frameworks like Monorail or the coming System.Web.MVC are the future way of programming web apps in .NET but it demands a considerable amount of effort to learn new frameworks. It’s difficult for someone like me who has invested lots of years in mastering the classical ASP.NET code-behind model to re-learn everything from scratch. In the meantime this should not be an excuse to not make my code more testable.

In this post I will explicit through a simple example how to use the model view controller pattern on top of the code-behind model. We will create a login form with the MVC pattern.

Setup your solution

Create a new solution “Zoo” with 3 projects –>

  • ZooWebsite-> ASP.NET web appplication
  • ZooLibrary -> Class library 
  • ZooTest - Class library
  • Create a reference from ZooWebsite to ZooLibrary

    (ZooWebsite , add reference, project tab select ZooLibrary)

  • On ZooLibrary add a reference to System.Web

ne project

 

The View

To make our code testable it’s very important to be able to decouple the UI from the ASP.NET code-behind.  Therefore we will create an interface our ASP.NET page should implement.  This View interface will represent the contract the UI as to conform to.  When we will test our controller we will not do this with our actual web page but through a mock object that implements the View interface.    

Add an interface ILoginView on the project ZooLibrary:

   1:  namespace ZooApplication.Library
   2:  {
   3:       public interface ILoginView
   4:      {
   5:          string ErrorMessage { get;set;}
   6:          string EmailAddress { get;set;}
   7:          string Password { get;set;}
   8:          void RedirectFromLoginPage();
   9:          System.Web.UI.WebControls.Button BtnLogin { get;set;}
  10:      }
  11:  }
  • Edit the default aspx page and enter: Welcome you are authenticated!
  • Add the login.aspx to the ZooWebsite project.
  • Edit the source of the login.aspx part -> add two textboxes, a button, and validators:
<%@ Page Language="C#" AutoEventWireup="true" Codebehind="Login.aspx.cs" Inherits="ZooApplication.Website.Login" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Login page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
                Login form<br />
                <asp:Label ID="LblErrorMsg" runat="server" Text="Invalid login" Visible="false" ></asp:Label><br />
                Email Address:
                <asp:TextBox ID="TxbEmailAddress" runat="server"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RfvEmailAddress" runat="server" ErrorMessage="Enter your email address!"
                    ControlToValidate="TxbEmailAddress">
                </asp:RequiredFieldValidator>
                <asp:RegularExpressionValidator ID="RevEmailAddress" runat="server" ControlToValidate="TxbEmailAddress"
                    ErrorMessage="Invalid email address!" ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
                </asp:RegularExpressionValidator></div>
            <div>
                Password:
                <asp:TextBox ID="TxbPassword" runat="server"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RfvPassword" runat="server" ErrorMessage="Enter your password!"
                    ControlToValidate="TxbPassword">
                </asp:RequiredFieldValidator>
            </div>
            <div>
                <asp:Button ID="PageBtnLogin" runat="server" Text="Login" />
            </div>
        </div>
    </form>
</body>
</html>

Because the code-behind is not testable and don’t make part of the SUT (Subject Under Test) we want to diminish the code-behind logic to a bare minimum.   The view responsibility is limited to output  the data coming from our model in a human readable way and to expose user input to the controller.

Therefore we implement our interface through an aspx page that only contains a set of properties that binds data coming from the controller with our web controls. 

Generally we will try to implement all the presentation logic into the controller. The only exception here will be the RedirectFromLoginPage() method.

   1:  using System;
   2:  using System.Data;
   3:  using System.Configuration;
   4:  using System.Collections;
   5:  using System.Web;
   6:  using System.Web.Security;
   7:  using System.Web.UI;
   8:  using System.Web.UI.WebControls;
   9:  using System.Web.UI.WebControls.WebParts;
  10:  using System.Web.UI.HtmlControls;
  11:  using ZooApplication.Library;
  12:   
  13:  namespace ZooApplication.Website
  14:  {
  15:      public partial class Login : System.Web.UI.Page, ZooApplication.Library.ILoginView
  16:      {
  17:   
  18:          public string EmailAddress
  19:          {
  20:              get
  21:              {
  22:                  return TxbEmailAddress.Text;
  23:              }
  24:              set
  25:              {
  26:                  TxbEmailAddress.Text = value;
  27:              }
  28:          }
  29:   
  30:          public string ErrorMessage
  31:          {
  32:              get
  33:              {
  34:                  return LblErrorMsg.Text;
  35:              }
  36:              set
  37:              {
  38:                  LblErrorMsg.Text = value;
  39:              }
  40:          }
  41:   
  42:          public string Password
  43:          {
  44:              get
  45:              {
  46:                  return TxbPassword.Text;
  47:              }
  48:              set
  49:              {
  50:                  TxbPassword.Text = value;
  51:              }
  52:          }
  53:   
  54:          public Button BtnLogin
  55:          {
  56:              get
  57:              {
  58:                  return PageBtnLogin;
  59:              }
  60:              set
  61:              {
  62:                  PageBtnLogin = value;
  63:              }
  64:          }
  65:   
  66:          public void RedirectFromLoginPage()
  67:          {
  68:              FormsAuthentication.RedirectFromLoginPage(this.EmailAddress, false);
  69:          }
  70:      }
  71:  }

 

The model

It’s our model that is responsible to validate the user login and password against the DB. 

We create a DB named ZooDB:

  • Add an APP_Data folder to your ZooWebsite project
  • APP_Data, new item, Database

Execute this script to create a table Profiles on the ZooDB:

CREATE TABLE [dbo].[Profiles](
      [ProfileID] [int] IDENTITY(1,1) NOT NULL,
      [EmailAddress] [nvarchar](255) NOT NULL,
      [Password] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_Profiles] PRIMARY KEY CLUSTERED
(
      [ProfileID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Now configure your web.config file to add the connectionstring and the authentication part:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="ZooDB" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=.\App_Data\ZooDB.mdf;Integrated Security=True"/>
  </connectionStrings>
    <system.web>
      <authentication mode="Forms">
        <forms name="AuthCookie" path="/" loginUrl="login.aspx" protection="All" timeout="10">
        </forms>
      </authentication>
      <authorization>
        <deny users="?"/>
      </authorization>
    </system.web>
</configuration>

image002

Test the application, it should compile and we should be redirected to the login page

image004

Our model implements an Authenticate method. Again we will make use of interfaces to decouple the model from the controller.

On the ZooLibrary project create an interface ILoginModel:

   1:  namespace ZooApplication.Library
   2:  {
   3:      public interface ILoginModel
   4:      {
   5:          bool Authenticate(string emailAddress, string password);
   6:      }
   7:  }

 

The method Authenticate of the class LoginModel will check the validity of supplied email address and password.  You can implement the model with your preferred data access code.  Personally I use Subsonic because it’s really simple to use and it’s based on the active record pattern, the same pattern used in Rails.

But for the moment let’s use standard ADO.NET code:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Data.SqlClient;
   5:  using System.Configuration;
   6:   
   7:  namespace ZooApplication.Library
   8:  {
   9:      public class LoginModel : ILoginModel
  10:      {
  11:          public bool Authenticate(string emailAddress, string password)
  12:          {
  13:              SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ZooDB"].ConnectionString);
  14:              SqlCommand cmd = new SqlCommand(
  15:                  "SELECT count(EmailAddress) FROM [Profiles] " +
  16:                  "WHERE EmailAddress=@EmailAddress AND Password=@Password",
  17:                  conn);
  18:              cmd.Parameters.AddWithValue("@EmailAddress", emailAddress);
  19:              cmd.Parameters.AddWithValue("@Password", password);
  20:              try
  21:              {
  22:                  conn.Open();
  23:                  if((int)cmd.ExecuteScalar()==1)
  24:                      return true;
  25:   
  26:                  return false;
  27:              }
  28:              finally
  29:              {
  30:                  conn.Close();
  31:              }
  32:          }
  33:      }
  34:  } 

The controller

The controller job is to figure out how the view should display the model. Therefore the controller should have an association with the model and the view.

  • Add a new class to ZooLibrary name it LoginController.

We start by defining a constructor that takes a ILoginView and a ILoginModel as parameters.
The Initialize method will be called by the page to instruct the controller to take control over the view and the model.
In the initialize method we will prepare the view to be rendered and subscribe to the events triggered by the view.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:   
   5:  namespace ZooApplication.Library
   6:  {
   7:      public class LoginController
   8:      {
   9:          private ILoginView _view;
  10:          private ILoginModel _model;
  11:         
  12:   
  13:          public LoginControler(ILoginView view, ILoginModel model)
  14:          {
  15:              this._view = view;
  16:              this._model = model;         
  17:          }
  18:   
  19:          public void Initialize()
  20:          {
  21:              this._view.ErrorMessage = "";
  22:              this._view.BtnLogin.Click += new EventHandler(BtnLogin_Click);
  23:          }
  24:   
  25:          public void BtnLogin_Click(object sender, EventArgs e)
  26:          {
  27:              if (this._model.Authenticate(this._view.EmailAddress, this._view.Password))
  28:                  this._view.RedirectFromLoginPage();
  29:              else
  30:                  this._view.ErrorMessage = "Invalid emailaddress or password!";
  31:          }
  32:      }
  33:  }

clip_image002

 

Integrating the MVC into the page

When we program against an asmx page it’s always the page that receive the initial control from the ASP.NET framework.
So it’s the page that need to instantiate the model, the view and the controller.

   1:      public partial class Login : System.Web.UI.Page, ZooApplication.Library.ILoginView
   2:      {
   3:          private LoginController controller;
   4:   
   5:          protected override void OnInit(EventArgs e)
   6:          {
   7:              base.OnInit(e);
   8:   
   9:              ILoginModel model = new LoginModel();
  10:              controller = new LoginController(this, model);
  11:          }
  12:          protected void Page_Load(object sender, EventArgs e)
  13:          {
  14:              controller.Initialize();
  15:          }
  16:  
  17:       }
 

Testing

We are now able with the help of mocking frameworks to test the logic in the model and the controller.

The code here uses NMock but you can use your prefered mocking framework to implement your tests:
In the meantime the test for our LoginController should look like this:

   1:  public void LoginController_LoginTest()
   2:          {
   3:              ILoginView view = mocks.NewMock<ILoginView>();
   4:   
   5:              ILoginModel model = mocks.NewMock<ILoginModel>();
   6:              LoginController target = new LoginController(view, model);
   7:             
   8:              Expect.AtLeastOnce.On(view).GetProperty("BtnLogin").Will(Return.Value(new System.Web.UI.WebControls.Button()));
   9:              Expect.AtLeastOnce.On(view).GetProperty("EmailAddress").Will(Return.Value("unitEmail@test.be"));
  10:              Expect.AtLeastOnce.On(view).GetProperty("Password").Will(Return.Value("password"));
  11:              Expect.Once.On(model).Method("Authenticate").With(view.EmailAddress, view.Password).Will(Return.Value(true));
  12:              Expect.Once.On(view).SetProperty("ErrorMessage").To("");
  13:              Expect.AtLeastOnce.On(view).Method("RedirectFromLoginPage");
  14:   
  15:              target.Initialize();
  16:              target.BtnLogin_Click(view.BtnLogin, null);
  17:   
  18:              mocks.VerifyAllExpectationsHaveBeenMet();
  19:             
  20:          }

I hope this introduction to the MVC pattern has been profitable to you.

 

kick it on DotNetKicks.com