4

Say, I have the below Controller

public class UsersController : Controller
{
   private IUsersRepository UsersRepository { get; }
   public UsersController()
   {
       UsersRepository = DependencyResolver.Current.GetService(typeof(IUsersRepository)) as IUsersRepository;
   }
   public ActionResult Index ()
   {
        MyUserDefinedModel data = UsersRepository.MyRepository();
        return View(data);
   }
}

Now I want to mock the IUsersRepository and pass it to the controller in my test script.

Below my test code

public class UsersListTest
   {
       private UsersController usersController = new Mock<IUsersRepository>();
       private Mock<IUsersRepository> usersRepository = new UsersController();
       [TestMethod]
       public void TestMethod1()
       {
           //usersRepository.Setup(x => x.Get()).Returns(users);
       }
   }

As because private IUsersRepository UsersRepository { get; } private, I'm not able to pass the mock of IUsersRepository.

What would be the good idea to write unit test and mock in such case.

Rahul Chakrabarty
  • 2,149
  • 7
  • 39
  • 70
  • Why don't you register your mock object to the DI container? it might be the simplest solution.... – Old Fox Apr 20 '16 at 21:46

2 Answers2

2

The reason that you have trouble with testing is because your Controller class uses the Service Locator anti-pattern. A Service Locator is a either a global instance (the DependencyResolver.Current) or an abstraction that allows resolving dependencies at runtime. One of the many downsides of the Service Locator is the problems it causes with testing.

You should move away from the Service Locator pattern and use dependency injection instead, favorably constructor injection. Your application components should have a single public constructor and those constructors should do nothing more than storing the incoming dependencies. This will result in the following UsersController implementation:

public class UsersController : Controller
{
   private IUsersRepository usersRepository;
   public UsersController(IUsersRepository usersRepository)
   {
       this.usersRepository = usersRepository;
   }
   public ActionResult Index()
   {
        return View(this.usersRepository.MyRepository());
   }
}

With this in place, unit testing became trivial:

public class UsersControllerTests
{
    [TestMethod]
    public void Index_Always_CallsRepository()
    {
        // Arrange
        var repository = new Mock<IUsersRepository>();
        var controller = CreateValidUsersController(repository.Instance);

        // Act
        var result = controller.Index();

        // Assert
        Assert.IsTrue(repository.IsCalled);
    }

    // Factory method to simplify creation of the class under test with its dependencies
    private UsersController CreateValidUsersController(params object[] deps) {
        return new UsersController(
            deps.OfType<IUsersRepository>().SingleOrDefault() ?? Fake<IUsersRepository>()
            // other dependencies here
            );
    }

    private static T Fake<T>() => (new Mock<T>()).Instance;
}

This does however, force you to change MVC's default IControllerFactory, since out-of-the-box, MVC can only handle controllers with a default constructor. But this is trivial and looks as follows:

public sealed class CompositionRoot : DefaultControllerFactory
{
    private static string connectionString = 
        ConfigurationManager.ConnectionStrings["app"].ConnectionString;

    protected override IController GetControllerInstance(RequestContext _, Type type) {

        if (type == typeof(UsersController))
            return new UsersController(new UsersRepository());

        // [other controllers here]

        return base.GetControllerInstance(_, type);
    }
}

Your new controller factory can be hooked into MVC as follows:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start() {
        ControllerBuilder.Current.SetControllerFactory(new CompositionRoot());

        // the usual stuff here
    }
}

You can find a more complete example here.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Agreed, but most DI containers already have a (nuget) package for integrating with ASP.NET. No need to write the ControllerFactory stuff yourself. – H H Apr 20 '16 at 11:38
  • @HenkHolterman: I don't think the OP is using a DI container. – Steven Apr 20 '16 at 11:43
  • But he should be be going to use one. – H H Apr 20 '16 at 12:00
  • @HenkHolterman: I don't agree. A DI container isn't a required tool. It's often more valuable to start with [Pure DI](http://blog.ploeh.dk/2014/06/10/pure-di/). (and do remember that this is the opinion of someone who maintains a DI container ;-)) – Steven Apr 20 '16 at 12:02
  • I'm not so convinced of the benefits of not using a container... And in [the next round](https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#dependency-injection) it's going to be hard to avoid. – H H Apr 20 '16 at 12:23
  • @HenkHolterman: Even though Microsoft is actively pushing us into the [Conforming Container anti-pattern](http://blog.ploeh.dk/2014/05/19/conforming-container/), even in 'the next round' it is really straightforward to apply Pure DI, since ASP.NET Core defines a few pretty good abstractions for intercepting the creation of your root types (e.g. the `IControllerActivator`). Never remember: a container is a tool: tools are never required for applying design patterns and principles. – Steven Apr 20 '16 at 12:32
1

You could add a constructor that allows you to supply a mock of IUsersRepository. Your default constructor would call this with the instance from the DependencyResolver, like this:

public class UsersController : Controller
{
   private IUsersRepository UsersRepository { get; }

    public UsersController(IUsersRepository usersRepository)
    {
        UsersRepository = usersRepository;
    }

    public UsersController():this(DependencyResolver.Current.GetService(typeof(IUsersRepository)) as IUsersRepository)
    {

    }

    public ActionResult Index ()
    {
       MyUserDefinedModel data = UsersRepository.MyRepository();
       return View(data);
    }

}

Mikael Nitell
  • 1,069
  • 6
  • 16