Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Simplify Automated Unit Testing Using IoC and MOQ

DZone's Guide to

Simplify Automated Unit Testing Using IoC and MOQ

In this article, one software developer shares his tried and true approach for testing websites after they migrate from web form to MVC.

· DevOps Zone
Free Resource

The Nexus Suite is uniquely architected for a DevOps native world and creates value early in the development pipeline, provides precise contextual controls at every phase, and accelerates DevOps innovation with automation you can trust. Read how in this ebook.

Introduction

A few years ago, I got the opportunity to work on a project that needed a migration from web form to MVC. I was assigned to come up with a good approach for testing the site. Afterward, I worked with various companies and I helped them to simplify unit testing using the same approach. It may be helpful to you, too.

The example code is in very simple form and you can improvise it as per your need. It is available here.

Background

You should have a knowledge of the following:

  • MOQ
  • IoC, i.e., the structure map used in the example
  • Unit testing frameworks, i.e., MS unit, N unit, Xunit, etc.
  • NBuilder or something similar like similar for generating mock data.

Take-Aways

  • How to use IoC to create mock objects.
  • How to maintain test cases with less effort.
  • How to help maintain uniformity in the code, which is very useful in Scrum-based projects.

Drawbacks of the Traditional Approach

  • There's lots of repeated code where we mock the dependencies.
  • Lots of effort is required to keep the test cases up to date.
  • It may lead to loss of focus on testing business cases properly.

About the Approach

Instead of mocking each method for each test and deciding the output, we will be using an in- memory database that will be generated using NBuilder. The dependencies will be handled by an IoC.

Also, we don’t mock the service layer method using the MOQ, but we use all the methods to call the repository layer, which is mocked to use the in-memory database.

Let’s start from the bottom up. The sample code covers User table which is available on GitHub here.

Code Explanation

1. In-Memory Mock Database

We need a mock data for each entity model that is mapped to a database table.

Create some mock records for the user table.

namespace SampleCode.IOC.Test.MockTestData
{
public class UserModelTestData
{
public static List<UserModel> GetTestRecords()
{
return Builder<UserModel>.CreateListOfSize(10).Build().ToList();
}
}
}

Create a class to store the mock records in a collection.

public static class MockEntities
{
public static Dictionary<string, dynamic> MockData = new Dictionary<string, dynamic>();
public static void LoadData()
{
MockData.Add(typeof(UserModel).Name, UserModelTestData.GetTestRecords());
}
public static List<T> GetData<T>() where T : class
{
return (List<T>)MockData[typeof(T).Name];
}
}

2. Create a Mock Repository Object Using MOQ

Here, we set up repository methods but instead of hard-coding the results, we do actual simple operations related to the methods. This approach uses lots of duplicate code and requires maintenance effort.

namespace SampleCode.IOC.Test
{
public static class MockRepositoryGenerator<Model> where Model : BaseModel
{
private static List<Model> DummyTable
{
get
{
return MockEntities.GetData<Model>();
}
}
public static T RepositoryMock<T>() where T:class,IBaseRepository<Model>
{
Mock<T> repository = new Mock<T>(MockBehavior.Strict);
repository.Setup(o => o.Get(It.IsAny<Expression<Func<Model, bool>>> ())).Returns((Expression<Func<Model, bool>> i) => DummyTable.Where(i.Compile()).FirstOrDefault());
return repository.Object;
}
}
}

3. IoC: Interface and Class Mapping

Here, we map interfaces with generated mock objects that also store mock data with them.

class StructureMapTestRegistry : Registry
{
/ <summary>
/ Initializes a new instance of the <see cref="DependencyConfigurationRegistry"/> class.
/ </summary>
public StructureMapTestRegistry()
{
MockEntities.LoadData();
For<IUserRepository>().Use(MockRepositoryGenerator<UserModel>.RepositoryMock<IUserRepository>());
For<IUnitOfWork>().Use(MockGenerator.UnitOfWorkMock());
For<IDataContext>().Use(MockGenerator.DataContextMock());
}
}

4. Create an IoC Container for the Test Project

public static class TestBootstrapper
{
public static void TestConfigureStructureMap()
{
ObjectFactory.Container.Dispose();
ObjectFactory.Initialize(o => o.AddRegistry(new StructureMapTestRegistry()));
ObjectFactory.Container.AssertConfigurationIsValid();
}
}

5. Writing Test Cases Will Be Quite Simpler

5.1. Test Cases for the Service Layer

[TestClass]
public class UserServiceTest
{
private IUserService domainService;
/ <summary>
/Initialize() is called once during test execution before
/test methods in this test class are executed.
/</summary>
[TestInitialize]
public void Initialize()
{
TestBootstrapper.TestConfigureStructureMap();
AutoMapperInit.BuildMap();
domainService = ObjectFactory.GetInstance<IUserService>();
domainService.UserRepository = ObjectFactory.GetInstance<IUserRepository>();
domainService.UnitOfWork = ObjectFactory.GetInstance<IUnitOfWork>();
}
[TestMethod]
public void Save_AddNewUser_returnsSaveUser()
{
var response = domainService.Save(new UserViewModel() { Id = 0, FirstName = "Mak11", LastName = "Vichare11" });
Assert.AreEqual("Vichare11", response.ViewModel.LastName);
}
[TestMethod]
public void Save_UpdateExistingUser_returnsSaveUser()
{
var response = domainService.GetById(5);
response.ViewModel.FirstName = "Makrand";
var newResponse = domainService.Save(response.ViewModel);
Assert.AreEqual(response.ViewModel.FirstName, newResponse.ViewModel.FirstName);
}
}

5.2. Test Cases for the Repository Layer

[TestClass]
public class UserRepositoryTest
{
private IUserRepository repository;
[TestInitialize]
public void Initialize()
{
TestBootstrapper.TestConfigureStructureMap();
repository = ObjectFactory.GetInstance<IUserRepository>();
}
[TestMethod]
public void Add_AddNewUser_returnsSaveUser()
{
var model = repository.Add(new UserModel() { Id =11, FirstName ="Mak" , LastName ="Vichare"});
Assert.AreEqual("Vichare", model.LastName);
}
[TestMethod]
public void Update_UpdateExistingUser_returnsSaveUser()
{
var oldModel = repository.GetById(5);
oldModel.FirstName = "Makrand";
var model = repository.Update(oldModel);
Assert.AreEqual(oldModel.FirstName, model.FirstName);
}
}

5.3. MVC Controller Test Cases

[TestClass]
public class UserControllerTest
{
private UserController userController;
IUserService userService;
[TestInitialize]
public void Initialize()
{
userService = ObjectFactory.GetInstance<IUserService>();
userController = new UserController(userService);
MockControllerHelpers.RegisterTestRoutes();
}
[TestMethod]
public void Edit_updates_the_object_and_returns_a_JsonResult_containing_the_redirect_URL()
{
Arrange
userController.SetMockController("~/User/Edit");
Act
var result = userController.Edit(1);
Assert
Assert.IsInstanceOfType(result, typeof(JsonResult));
}
}

Final Thoughts

In real life, this approach can be implemented various ways. You don't have to implement it completely. If don't agree with this approach 100%, then you can at least pick up few things like using IoC for the creation of mock object or repository layer test cases.

Let me know what you think and would suggest about this style.

The DevOps Zone is brought to you in partnership with Sonatype Nexus.  See how the Nexus platform infuses precise open source component intelligence into the DevOps pipeline early, everywhere, and at scale. Read how in this ebook

Topics:
devops ,testing automation ,unit testing ,ioc ,moq

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}