Using a Unity Container with Dependency Injection for Unit Testing with ASP.NET MVC
Using a Unity Container or Service Locator with Dependency Injection for Unit Testing.
Introduction
I’m currently working on an ASP.NET MVC prototype that I’m going to demonstrate Unity Testing a Controller which uses Dependency Injection.
In my case, I’m using ASP.NET MVC controllers that have a dependency on certain actions. I wanted to keep the Controller as clean as possible, so I created an IAction
interface. Let’s call it HomeController
for now.
/// <summary>
/// HomeController class.
/// </summary>
public class HomeController : SecuredBaseController
{
/// <summary>
/// Search Action Interface.
/// </summary>
private ISearchAction searchAction;
/// <summary>
/// Constructor for Home Controller.
/// </summary>
/// <param name="searchAction">Home page search action.</param>
public HomeController(ISearchAction searchAction)
{
this.searchAction = searchAction;
}
/// <summary>
/// Load view Index.
/// </summary>
/// <returns>Action Result.</returns>
public ActionResult Home()
{
ViewData["Title"] = "Summary";
ViewData["Message"] = "Welcome to ASP.NET MVC!";
//// get valid review status
SubmissionStatus[] submissionStatusList =
this.searchAction.GetSubmissionStatusForAuditor();
SelectList reviewStatusList = new SelectList(submissionStatusList, "Id",
"InternalDisplayName");
ViewData["ReviewStatusId"] = reviewStatusList;
//// primary countries
CountryRegion[] countryList = this.searchAction.GetCountries();
SelectList countrySelectList = new SelectList(countryList, "Id", "Name");
ViewData["CountryId"] = countrySelectList;
//// get programs
Program[] programs = this.searchAction.GetFilteredProgramList();
SelectList programSelectList = new SelectList(programs, "Id", "Name");
ViewData["ProgramId"] = programSelectList;
//// get activity types
Item[] activityTypes = this.searchAction.GetActivityTypesForAuditor();
SelectList activityTypesSelectList = new SelectList(activityTypes, "Id", "Name");
ViewData["ActivityTypeId"] = activityTypesSelectList;
return View("ViewPoeSubmissions");
}
You can see that we have a search page that calls a Search Action interface with all the code contained in a SearchAction
class. The SearchAction
class is in a, you guessed it.. Unity Container. I won’t go into the factory class that creates the Unity Container right now. I’ll write another article on that item, but for now, the Unity Container is actually created on Application_Start()
and looks something like: ControllerBuilder.Current.SetControllerFactory(UnityContainers.Instance.Resolve<IControllerFactory>());
.
The Solution
How do we test this? As I mentioned above, the Unity Container is created on Application_Start()
for our website, but since the controller takes an Interface, I can mock that up using the Repository pattern in the Unit Tests.
Here is what the SearchAction
class looks like:
/// <summary>
/// Search action class for the controller.
/// </summary>
public class SearchAction : IPoeSearchAction
{
/// <summary>
/// Taxonomy model interface.
/// </summary>
private ITaxonomyModel taxonomyModel;
/// <summary>
/// Activity request model interface.
/// </summary>
private IRequestModel requestModel;
/// <summary>
/// Search Action.
/// </summary>
/// <param name="taxonomyModel">Taxonomy Model Interface.</param>
/// <param name="requestModel">Request Model Interface.</param>
/// public SearchAction(ITaxonomyModel taxonomyModel, IRequestModel requestModel)
{
this.taxonomyModel = taxonomyModel;
this.requestModel = requestModel;
}
Notice that the constructor for SearchAction
takes two Interfaces: ItaxonomyModel
and IrequestModel
.
The GetCountries()
method of the SearchAction
class looks like this:
/// <summary>
/// Get list of primary countries.
/// </summary>
/// <returns>Country region collection.</returns>
public CountryRegion[] GetCountries()
{
// Grab the Instance from Activity Request Process.
return this.taxonomyModel.GetCountries();
}
It’s actually the TaxonomyModel
class that calls the Web Service and takes care of the GetCountries()
code, so I can create a class called TestTaxonomyModel
that implements the ItaxonomyModel
interface, and in that test class, I can put mock code in the GetCountries
method that will return a subset of the test data.
The RequestModel
class works the same way.
/// <summary>
/// Search for a Claim.
/// </summary>
/// <param name="form">Form Collection.</param>
/// <returns>Activity Based Claim Search Result.</returns>
public ClaimSearchResult Search(FormCollection form)
{
/// Create some search criteria from the form data.
ClaimSearchCriteria criteria = this.CreateSearchCriteria(form);
return this.requestModel.SearchClaims(searchCriteria);
}
I can create a class called TestRequestModel
that implements the IRequestModel
interface, and put logic to return test data based upon the search criterion passed in.
Here is what my Unit Test looks like. First, I initialize a Unity Container with test data:
/// <summary>
/// Internal chip service locator.
/// </summary>
private IServiceLocator serviceLocator;
/// <summary>
/// Unity container.
/// </summary>
private IUnityContainer unityContainer;
/// Initialize tests.
/// </summary>
TestInitialize]
public void InitializeTests()
{
this.unityContainer = new UnityContainer()
.RegisterType<IController, HomeController>()
.RegisterType<ISearchAction, SearchAction>();
this.unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<SearchAction>(new InjectionConstructor(new object[] {
new TestTaxonomyModel(), new TestRequestModel() }))
.ConfigureInjectionFor<HomeController>(new InjectionConstructor(
this.unityContainer.Resolve<ISearchAction>()));
unityContainer.Resolve<IController>();
this.serviceLocator = new MyServiceLocator(this.unityContainer);
}
/// <summary>
/// Search test method.
/// </summary>
[TestMethod]
public void SearchTest()
{
var homeController = new HomeController(
this.serviceLocator.GetInstance<SearchAction>());
FormCollection form = new FormCollection();
form["ProgramId"] = "3";
form["ActivityId"] = "8";
form["CountryId"] = "33";
form["Filter"] = "3";
form["Region"] = "Foo";
searchController.Search(form);
}
There, you can see that we call the Search
method on the search controller, passing it the form we created, and the code will be applied against our test controllers.
Read more about this at my website.