Search code examples
c#unit-testingasp.net-coretestingdependency-injection

how to unit test asp.net core application with constructor dependency injection


I have a asp.net core application that uses dependency injection defined in the startup.cs class of the application:

    public void ConfigureServices(IServiceCollection services)
    {

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration["Data:FotballConnection:DefaultConnection"]));


        // Repositories
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IUserRoleRepository, UserRoleRepository>();
        services.AddScoped<IRoleRepository, RoleRepository>();
        services.AddScoped<ILoggingRepository, LoggingRepository>();

        // Services
        services.AddScoped<IMembershipService, MembershipService>();
        services.AddScoped<IEncryptionService, EncryptionService>();

        // new repos
        services.AddScoped<IMatchService, MatchService>();
        services.AddScoped<IMatchRepository, MatchRepository>();
        services.AddScoped<IMatchBetRepository, MatchBetRepository>();
        services.AddScoped<ITeamRepository, TeamRepository>();

        services.AddScoped<IFootballAPI, FootballAPIService>();

This allows something like this:

[Route("api/[controller]")]
public class MatchController : AuthorizedController
{
    private readonly IMatchService _matchService;
    private readonly IMatchRepository _matchRepository;
    private readonly IMatchBetRepository _matchBetRepository;
    private readonly IUserRepository _userRepository;
    private readonly ILoggingRepository _loggingRepository;

    public MatchController(IMatchService matchService, IMatchRepository matchRepository, IMatchBetRepository matchBetRepository, ILoggingRepository loggingRepository, IUserRepository userRepository)
    {
        _matchService = matchService;
        _matchRepository = matchRepository;
        _matchBetRepository = matchBetRepository;
        _userRepository = userRepository;
        _loggingRepository = loggingRepository;
    }

This is very neat. But becomes a problem when I want to unit test. Because my test library does not have a startup.cs where I setup dependency injection. So a class with these interfaces as params will just be null.

namespace TestLibrary
{
    public class FootballAPIService
    {
        private readonly IMatchRepository _matchRepository;
        private readonly ITeamRepository _teamRepository;

        public FootballAPIService(IMatchRepository matchRepository, ITeamRepository teamRepository)

        {
            _matchRepository = matchRepository;
            _teamRepository = teamRepository;

In the code above, in the test library, _matchRepository and _teamRepository, will just be null. :(

Can I do something like ConfigureServices, where I define dependency injection in my test library project?


Solution

  • Your controllers in .net core have dependency injection in mind from the start, but this does not mean you are required to use a dependency injection container.

    Given a simpler class like:

    public class MyController : Controller
    {
    
        private readonly IMyInterface _myInterface;
    
        public MyController(IMyInterface myInterface)
        {
            _myInterface = myInterface;
        }
    
        public JsonResult Get()
        {
            return Json(_myInterface.Get());
        }
    }
    
    public interface IMyInterface
    {
        IEnumerable<MyObject> Get();
    }
    
    public class MyClass : IMyInterface
    {
        public IEnumerable<MyObject> Get()
        {
            // implementation
        }
    }
    

    So in your app, you're using the dependency injection container in your startup.cs, which does nothing more than provide a concretion of MyClass to use when IMyInterface is encountered. This does not mean it is the only way of getting instances of MyController however.

    In a unit testing scenario, you can (and should) provide your own implementation (or mock/stub/fake) of IMyInterface as so:

    public class MyTestClass : IMyInterface
    {
        public IEnumerable<MyObject> Get()
        {
            List<MyObject> list = new List<MyObject>();
            // populate list
            return list;
        }        
    }
    

    and in your test:

    [TestClass]
    public class MyControllerTests
    {
    
        MyController _systemUnderTest;
        IMyInterface _myInterface;
    
        [TestInitialize]
        public void Setup()
        {
            _myInterface = new MyTestClass();
            _systemUnderTest = new MyController(_myInterface);
        }
    
    }
    

    So for the scope of unit testing MyController, the actual implementation of IMyInterface does not matter (and should not matter), only the interface itself matters. We have provided a "fake" implementation of IMyInterface through MyTestClass, but you could also do this with a mock like through Moq or RhinoMocks.

    Bottom line, you do not actually need the dependency injection container to accomplish your tests, only a separate, controllable, implementation/mock/stub/fake of your tested classes dependencies.