Search code examples
c#unit-testingasp.net-coreasp.net-core-2.0mstest

Declaring constructor inside Controller inheriting ControllerBase


I'm working on web application project which uses ASP.NET Core 2.1. Along with developing the API we are also trying to Unit test it using MSTest framework.

My Controllers are inheriting from ControllerBase. In my Test Bench I'm mocking my Business Layer using Moq Framework. When I'm calling the Controller from the test method, I need to pass a Mocked Business instance to the controller, for which I'm trying to declare parameterized constructor.

It's working fine for test cases, but my normal flow is disturbed. I even tried using both parameterized and parameterless constructors.

This works fine with Dot Framework which inherits APIController.

public class BookingController: ControllerBase {
    BusinessManager business = new BusinessManager();
    //Non-Parameterized Constructor
    public BookingController() {}
    //Parameterized Constructor
    public BookingController(BusinessManager mockedBusiness) {
        this.business = mockedBusiness;
    }
}

A non-parameterized constructor should be used when called from UI. Parameterized should only work when called from Test Bench passing some instance.


Solution

  • In the original code, the

    BusinessManager business = new BusinessManager();
    

    was tightly coupling the controller to the dependency, and is considered a code smell. Which is why you ended up having to try a work around in order to be able to test the controller in isolation.

    Use the explicit dependency principle and keep the parameterized constructor

    public class BookingController: ControllerBase {
        private readonly BusinessManager business;
    
        //Parameterized Constructor
        public BookingController(BusinessManager business) {
            this.business = business;
        }
    
        //...
    }
    

    In Startup, register you dependency with the service collection

    public void ConfigureServices(IServiceCollection services) {
    
        //...
    
        services.AddScoped<BusinessManager>();
    
        //...
    
    }
    

    The will allow the framework to inject the required dependency at run time when the controller is created in your normal flow, and also allows the controller to be flexible enough to be tested in isolation with your mocked business instance.