learn forever RSS 2.0
# Friday, December 25, 2009

Testing is Always Number One

If you read any book on why using an IoC (Inversion of Control) approach to your website using tools like Microsoft’s Unity Application Block and a Mocking Framework like Rhino Mocks, the number one reason is going to be writing solid unit tests, usually using a practice like TDD.     While I have no reason to doubt that that should be the number one reason, I like to always try and find more “live and in the moment” scenarios for using these tools.  

Here’s a very common testing scenario and implementation for testing that the Google Analytics javascript is only appearing in my site if I am in a Production environment.   This makes also makes use of MvcContrib.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MyWebSite.Web;
using MyWebSite.Web.Controllers;
using MyWebSite.Services;
using Rhino.Mocks;
using MvcContrib.TestHelper;
public class HomeControllerTest
   {
       IAppConfig _appConfig;
       IEmailService _emailService;
       HomeController _homeController;

       [TestInitialize]
       public void Init()
       {
           _appConfig = MockRepository.GenerateStub<IAppConfig>();
           _emailService = MockRepository.GenerateStub<IEmailService>();

           _homeController = new HomeController(_appConfig, _emailService);
           TestControllerBuilder builder = new TestControllerBuilder();
           builder.InitializeController(_homeController);
       }
      
       [TestMethod]
       public void Analytics_NotProduction()
       {
           // Arrange
           _appConfig.Stub(a => a.Environment).Return("Staging");

           // Act
           EmptyResult result = _homeController.Analytics() as EmptyResult;

           // Assert
           Assert.IsNotNull(result);
       }

       [TestMethod]
       public void Analytics_Production()
       {
           // Arrange
           _appConfig.Stub(a => a.Environment).Return("Production");

           // Act
           ViewResult result = _homeController.Analytics() as ViewResult;

           // Assert
           Assert.IsNotNull(result);
       }
   }

 

So here, my HomeController class’ constructor needs an instance of both IEmailService and IAppConfig.  Rhino Mocks creates a mock of both of those interfaces and additionally sets the “Environment” property of the mocked IAppConfig instance to either “Staging” or “Production” depending on my test.  

In the “Analytics_NotProduction” test, my test passes if an “EmptyResult” instance is returned by my HomeController’s  Analytics method meaning that no View is generated with any markup or javascript of any kind.   The “Analytics_Production” test passes if the Analytics method returns an actual ViewResult which presumably generates the javascript I need for Google Analytics.

Again, this is just your typical unit test scenario.  This is great, you should do this.  I should do this more than I do right now with my projects.  That’s another story.  Here’s another quick and easy (and also immediately useful) scenario I’ve come up with for using IoC.

Shared Hosting Limitations with a Staging and Production Environment

So recently, I’ve begun working on my wife’s business website, Jamie Lynn Designs.  This website is currently hosted using Softsys Hosting’s Shared Hosting.    /* End shameful social marketting plug */

On this website, random quotes are displayed from my wife’s happy customers.  Every time you load up a page, another random quote displays.  These quotes are stored in a SQL Database.   

Snippet from HomeController.cs

   private IAppConfig _appConfig;
       private IClientQuoteService _clientQuoteService;
       private IContactSubmissionService _contactSubmissionService;

       public HomeController(IAppConfig appConfig, IContactSubmissionService contactSubmissionService, 
IClientQuoteService clientQuoteService) { _appConfig = appConfig; _contactSubmissionService = contactSubmissionService; _clientQuoteService = clientQuoteService; }
public ActionResult RandomQuote()
       {
           ClientQuote clientQuote = _clientQuoteService.GetRandomClientQuote();
           return View(clientQuote);
       }

As you see, in the “RandomQuote” method, there is a call to the IClientQuoteService’s “GetRandomClientQuote” method.  Using Unity, a concrete implementation of this interface called “ClientQuoteService” is used.

Snippet from ClientQuoteService.cs

public class ClientQuoteService:IClientQuoteService
    {
        IRepository _repository;

        public ClientQuoteService(IRepository repository)
        {
            _repository = repository;
        }

        #region IClientQuoteService Members

        public ClientQuote GetRandomClientQuote()
        {
            int clientQuoteCount = _repository.GetAll<ClientQuote>().Count();
            int clientQuoteIndex = new Random().Next(clientQuoteCount);

            return _repository.GetAll<ClientQuote>().Skip(clientQuoteIndex).First();
        }

        #endregion
    }

 

This website currently lives in 3 different environments.  My local (development) environment, a Staging Environment also hosted by Softsys Hosting, and the Production Environment.   Since I’m limited to a certain number of databases with my shared hosting plan, I didn’t want to go and create a Staging database for the website.  I was trying to think of some ways to “physically” handle this problem. 

 

First, I thought about using the Production database for Staging and just creating “Staging” tables that mimicked production. So if I had a “ClientQuote” table in Production, I could have a “Staging_ClientQuote” table for Staging.   Doing that would be wrong on so many levels. The most annoying being database migrations and creating different entity classes for all my database tied entities.

 

So instead, I stepped back and looked at what was really going on in my application to generate these quotes.  The only thing my HomeController needed in it’s RandomQuote method was an instance of “ClientQuote”.   What I really needed to do was provide an alternative way to get this quote in an environment that doesn’t use a database.  I needed ANOTHER concrete class to be used in my staging environment.  Enter StagingClientQuoteService.

 

Snippet from StagingClientQuoteService.cs

 

public class StagingClientQuoteService : IClientQuoteService
   {
       #region IClientQuoteService Members

       public ClientQuote GetRandomClientQuote()
       {
           return new ClientQuote
           {
               City = "Chicago",
               Id = 1,
               Quote = "Jamie Lynn Designs is really amazing! What else can I say?",
               State = "IL",
               SubmittedBy = "Dave"
           };
       }

       #endregion
   }

 

So here, I now have a class that just creates the same ClientQuote over and over each time it is called and returns it.   I could’ve gotten more complex and created some more “database free” random quotes and returned those instead.

 

Using the extension Unity.Configuration block, I’m able to easily swap between my “real” ClientQuoteService and StagingClientQuoteService.  Below I’m omitting some of the other Unity Mappings to just show what I did for the IClientQuoteService interface.

 

 

Snippet from PRODUCTION web.config

 

<unity>
    <containers>
      <container>
        <types>
          <type
             type="JamieLynnDesigns.Core.Services.IClientQuoteService,JamieLynnDesigns.Core"
             mapTo="JamieLynnDesigns.Core.Services.ClientQuoteService,JamieLynnDesigns.Core">
          type>
         types>
      container>
    containers>
  unity>

 

Snippet from STAGING web.config

<unity>
    <containers>
      <container>
        <types>
          <type
           type="JamieLynnDesigns.Core.Services.IClientQuoteService,JamieLynnDesigns.Core"
           mapTo="JamieLynnDesigns.Core.Services.Fake.StagingClientQuoteService,JamieLynnDesigns.Core">
          type>
        types>
      container>
    containers>
  unity>

 

And that’s it!  There are many other scenarios where this kind of thing comes in useful.  The only thing I am watching out for is creating too many of my own “fake” classes, so my projects don’t come to large with “non production” code.  But as long as there’s a good logical separation, such as a folder named “Staging Mocks” or something of the sort in your project, you should be okay.

Friday, December 25, 2009 8:11:14 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
ASP.Net MVC | Inversion of Control (IoC) | Unity Application Block
Dave Arlin
Archive
<July 2014>
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789
About the author/Disclaimer

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

© Copyright 2014
Dave Arlin
Sign In
Statistics
Total Posts: 12
This Year: 0
This Month: 0
This Week: 0
Comments: 1
All Content © 2014, Dave Arlin
DasBlog theme 'Business' created by Christoph De Baene (delarou)