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?


Although @Kritner's answer is correct, I prefer the following for code integrity and better DI experience:

[TestClass]
public class MatchRepositoryTests
{
    private readonly IMatchRepository matchRepository;

    public MatchRepositoryTests()
    {
        var services = new ServiceCollection();
        services.AddTransient<IMatchRepository, MatchRepositoryStub>();

        var serviceProvider = services.BuildServiceProvider();

        matchRepository = serviceProvider.GetService<IMatchRepository>();
    }
}

A simple way, I wrote a generic dependency resolver helper class and then built the IWebHost in my unit test class.

Generic Dependency Resolver

        using Microsoft.AspNetCore.Hosting;
        using Microsoft.AspNetCore.Mvc;
        using Microsoft.Extensions.Configuration;
        using Microsoft.Extensions.DependencyInjection;
        using Microsoft.Extensions.Hosting;
        public class DependencyResolverHelper
        {
            private readonly IWebHost _webHost;
    
            /// <inheritdoc />
            public DependencyResolverHelper(IWebHost webHost) => _webHost = webHost;
    
            public T GetService<T>()
            {
                var serviceScope = _webHost.Services.CreateScope();
                var services = serviceScope.ServiceProvider;
                try
                {
                  var scopedService = services.GetRequiredService<T>();
                  return scopedService;
                }
                catch (Exception e)
                {
                   Console.WriteLine(e);
                   throw;
                }
            }
        }
    }

Unit Test Project:

      [TestFixture]
        public class DependencyResolverTests
        {
            private DependencyResolverHelper _serviceProvider;

            public DependencyResolverTests()
            {

                var webHost = WebHost.CreateDefaultBuilder()
                    .UseStartup<Startup>()
                    .Build();
                _serviceProvider = new DependencyResolverHelper(webHost);
            }
    
            [Test]
            public void Service_Should_Get_Resolved()
            {
                
                //Act
                var YourService = _serviceProvider.GetService<IYourService>();
    
                //Assert
                Assert.IsNotNull(YourService);
            }
    

        }

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.


If you are using the Program.cs + Startup.cs convention and want to get this working quickly you can reuse your existing host builder with a one-liner:

using MyWebProjectNamespace;

public class MyTests
{
    readonly IServiceProvider _services = 
        Program.CreateHostBuilder(new string[] { }).Build().Services; // one liner

    [Test]
    public void GetMyTest()
    {
        var myService = _services.GetRequiredService<IMyService>();
        Assert.IsNotNull(myService);
    }
}

Sample Program.cs file from web project:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace MyWebProjectNamespace
{
    public class Program
    {
        public static void Main(string[] args) =>
            CreateHostBuilder(args).Build().Run();

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}