2

Each time i read an article or a book about asp.net MVC , there will be a chapter for dependency injection and repository . now i can easily feel the benefits of using repository over hard coding the entity framework code inside the action methods, as using repository i can write the methods which uses the entity framework inside my repository methods and reuse these methods over my action method, so i can easily understand that using Repository will facilitate re-usability .

but when it comes to dependency injection, authors always say that it facilitate testing and unit testing. so they say instead of initiating the repository class inside the controller class i will be initiating an interface of the repository class. and they say this will facilitate the unit testing.. but i can not get my mind on this , mainly why imitating the repository interface inside my controller class will facilitate unit testing while initiating the repository class itself inside my controller class will not !!

can anyone adivce on this?

5
  • Possible duplicate of (Why) is it important that a unit test not test dependencies?
    – gnat
    Commented Jun 26, 2017 at 16:04
  • 1
    @gnat: That's not even close to be a duplicate. Commented Jun 26, 2017 at 16:37
  • 1
    Dependency injection is a valid technique and probably needed more in web applications than elsewhere, but most of its promotion is hype caused by certain authors, and this trend will pass, like others. Commented Jun 26, 2017 at 17:33
  • IMO, unit testing a controller is largely a waste of time; at least if you've properly isolated the business logic away from the controller. Commented Jun 26, 2017 at 17:43
  • 1
    @TheCatWhisperer: I agree that unit tests for controllers are not that helpful. Bugs crop up more often in permissions checks and business logic - both of which should not be handled by the controller. Automated functional tests are better for testing controllers. Commented Jun 26, 2017 at 18:16

2 Answers 2

3

Consider a simple blog, and a PostsController that shows a blog post.

You enable dependency injection by setting the IPostRepository object using a constructor argument and instead of instantiating a concrete class in the controller:

public class PostsController : Controller
{
    private IPostRepository posts;

    public PostsController(IPostRepository posts)
    {
        if (posts == null)
            throw new ArgumentNullException("posts");

        this.posts = posts;
    }

    public ActionResult Details(int id)
    {
        BlogPost post = posts.Find(id);

        if (post == null)
            return HttpNotFound();

        BlogPostDetails model = new BlogPostDetails(post);

        return View(model);
    }
}

By allowing the posts repository to be injected, we can unit test the logic in the controller by providing a "mock" object. In this case, I'm using Moq, an object mocking library available on NuGet (not an endorsement, just for the sake of completing the example):

[TestClass]
public class BlogPostTests
{
    private IPostRepository mockRepository;

    [TestInitialize]
    public void Setup()
    {
        this.mockRepository = new Moq.Mock<IPostRepository>();
    }

    [TestMethod]
    public void ReturnsView()
    {
        var expectedPost = new BlogPost("Test Title", "<p>Body text</p>", DateTime.Parse("2017/01/13"));

        mockRepository.Setup(posts => posts.Find(1))
            .Returns(expectedPost) // posts.Find(1) will return a test stub
            .Verifiable(); // If posts.Find(1) doesn't get called, test fails

        var controller = new PostsController(mockRepository.Object);
        var result = controller.Details(1);

        // Assert that correct view was returned
    }
}

Now you can disconnect your controller from the rest of the technology stack and just test the "Details" method, making this a true Unit Test. As an added benefit, this test will execute in a millisecond or less. Compare that to a manual functional test run by a human, that will probably take upwards of a minute to set up the data and verify it, or 20-30 seconds for an automated functional test. The unit test will execute faster by an order of magnitude.

4
  • ok i start understanding it.. but now let say the above 2 classes are inside my real asp.net MVC project. so i will have 2 repository classes; Test and the Real repository classes and both of them inherit from the IRepository() class, so now when i write this inside my controller class private IPostRepository posts; BlogPost post = posts.Find(id); so on run-time which Find() method the controller class will use the one inside the test repository or the one inside the real repository ??
    – test test
    Commented Jun 27, 2017 at 11:28
  • 1
    @johnG: You don't need two repository classes if you use an object mocking library. If you don't want to use an object mocking library, then you would need two repository classes that implement the same interface. Commented Jun 27, 2017 at 13:59
  • let say i did not use object mocking, so i will end up creating two repositories Test & Live. and the two repository classes will be refereeing the IRepository... so if inside my controller class, i create a new IRepository object and i define to use a method named GetAllUsers() so which GetAllUsers() method will be used the one inside the Test or the one inside the real repository ?
    – test test
    Commented Jun 28, 2017 at 15:53
  • @johnGu: you will need to implement GetAllUsers() in the test repository as well (I'm a little late getting back to you on this comment, sorry). Commented Oct 2, 2021 at 13:28
2

The point of unit testing is to isolate away any dependencies. Otherwise you are not unit testing, you are integration testing.

The problem

So how do you isolate away your dependencies so you are only testing the code of interest? Well let's take a look.

Consider the following example controller/action:

class MyController
{
    public ActionResult MyAction()
    {
        if (HttpContext.Current.Request.RawUrl.Contains("SomeString")
        {
            return Redirect("/Someurl");
        }
        return View();
    }
}

This is terrible code for a number of reasons (model binding, anyone?)... but for the present discussion, set that aside, and just note that HttpContext is created by the action method via a sort of singleton pattern. It's not injected, and there is no way to change it, other than to modify your code, or do something really weird with the .NET profiling API, like TypeMock does. Without heroic efforts, you are stuck using the real HttpContext object in the .NET runtime.

Now imagine you want to write a unit test to see if the action method does indeed perform a redirect when the URL contains "SomeString". how would you do it? Where do you get the HttpContext? You need a real one. So do you spin up a web server on your unit testing machine? Invoke it with a web client? Will you need to sign on? Create cookies? Ugh.

Bottom line: You have no way to isolate away the dependency on HttpContext.

A better way

If we were to modernize this with a bit of DI, it might look like this:

class MyController
{
    private readonly HttpContextBase _httpContext;

    public MyController(HttpContextBase context)  //Injected
    {
        _httpContext = context;
    }

    public ActionResult MyAction()
    {
        if (_httpContext.Request.RawUrl.Contains("SomeString")
        {
            return Redirect("/Someurl");
        }
        return View();
    }
}

Now we can test this, by supplying a mockup of the HTTP context that conforms to HttpContextBase's interface, like this:

class MockHttpRequest: System.Web.HttpRequestBase
{
    public override string RawUrl
    {
        get
        {
            return "SomeString";
        }
    }
}
class MockHttpContext : System.Web.HttpContextBase
{
    public override System.Web.HttpRequestBase Request
    {
        get
        {
            return new MockHttpRequest();
        }
    }
}

And in your unit test code:

//Arrange
var context = new MockHttpContext();
var controller = new MyController(context);

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

//Assert
Assert.IsTrue(result is RedirectResult);

Piece of cake. Don't need a web server. Don't even need Moq or TypeMock or Fakes. You can do it all yourself. It would not only be harder, but it would be impossible the traditional way, without a mocking framework.

P.S.

Incidentally, Microsoft thought this was so valuable they added HttpContextBase just so that it could be injected and overridden in unit tests (the regular HttpContext is sealed). If they went to the trouble to add it to the runtime, it's probably worth using.

2
  • your example just lead me to the right path to start to understanding the real benefits of DI...but my question now .. let say i have created two repository classes RealRepo & TestRepo and both of them will be implementing the IRepository class.. now if inside my controller class i define an new IReposiory object, then i want to use one of its methods.. where this method will be implemented inside the Real and inside the Test repositories ,,, so which method the controller will be working with ??
    – test test
    Commented Jun 28, 2017 at 16:01
  • 1
    The controller will work with whichever IRepository was injected when it was constructed. In production mode, you should have registered RealRepo in your composition root (bootstrapper.cs, in MVC, usually), e.g. container.RegisterType<IRepository, RealRepo>();. And in your test harness you just inject TestRepo directly.
    – John Wu
    Commented Jun 28, 2017 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.