Question

In the context of unit testing it is possible to create two controller constructors, one that the controller factory will use by default and another one dedicated for unit testing.

public class ProductController : Controller 
{
  private IProductRepository repository;
  public int PageSize = 10;

  // default constructor
  public ProductController() 
  {
    this.repository = new ProductRepository();
  }

  // dedicated for unit testing
  public ProductController(IProductRepository productRepository) 
  {
    this.repository = productRepository;
  }

  public ViewResult List(int page=1) 
  {
    return View(repository.Products
       .OrderBy(p => p.ProductID)
       .Skip((page - 1) * PageSize)
       .Take(PageSize));
  }
}

Unit testing can be achieved this way

[TestMethod]
public void Can_Paginate() 
{
  // Arrange
  Mock<IProductRepository> mock = new Mock<IProductRepository>();
  mock.Setup(m => m.Products).Returns(new Product[] 
  {
    new Product {ProductID = 1, Name = "P1"},
    new Product {ProductID = 2, Name = "P2"},
    new Product {ProductID = 3, Name = "P3"},
    new Product {ProductID = 4, Name = "P4"},
    new Product {ProductID = 5, Name = "P5"}
  }.AsQueryable());

  ProductController controller = new ProductController(mock.Object);
  controller.PageSize = 3;

  // Act
  IEnumerable<Product> result =
  (IEnumerable<Product>)controller.List(2).Model;
  // Assert
  Product[] prodArray = result.ToArray();
  Assert.IsTrue(prodArray.Length == 2);
  Assert.AreEqual(prodArray[0].Name, "P4");
  Assert.AreEqual(prodArray[1].Name, "P5");
}

In the above I was able to achieve unit testing. My question is why will I choose to use a DI framework (eg. Unity, Ninject, etc.) if I can achieve DI using a dedicated constructor? I must be missing something obvious here.

(BTW the code samples above are mostly from the book Pro ASP.NET MVC 4 book by Adam Freeman, and I modified it some to suit the question I needed to ask)

Was it helpful?

Solution

In this trivial example.. technically there is no reason.

The point of the frameworks is to globally manage dependencies and various parts of their existence... not just to inject dependencies. Lifetime/scope, how/when, where/why. What you have here is still tightly coupled to ProductRepository... and only that.

In a complex application, what you currently have requires something like this:

this.repository = new ProductRepository(
                          new ShoppingCartRepository(
                                  new Logger()),
                          new ReviewsRepository(
                                  new Logger()),
                          new Logger());

..whereas with a DI/IoC framework, you don't have to worry about any of that wiring up and all of the underlying lifetime/connection logic.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top