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?
new SUT(mockDependency);
is fine for your test.
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);
}
}
Once Dispose is called, any scoped services that have been resolved from ServiceProvider will be disposed.
.
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.
ServiceCollection
from the start. It's particularly helpful for scaffolding, and it's helpful for integration tests too so... yeah I'd be FOR using DI in your tests.
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>();
});
}
}
[SetUp]
could be useful, or even mocking the dependencies. But really, what I wanna do is use the same service collection used by my web app, and run tests against the same environment. Cheers!
You can use asp.net core DI and inject mocked instance objects in your tests. Here is a full working example :
For the sake of the example :
I only kept the IMatchService dependency from the code snippet of the initial question
I added a DoSomething action in the MatchController so that there is someting to test.
I added an Add method to the IMatchService and the MatchService classes so that there is soemthing to mock.
Please note that the methods that will have a Setup
with Moq
should be virtual.
[Route("api/[controller]")]
public class MatchController : AuthorizedController
{
private readonly IMatchService _matchService;
public MatchController(IMatchService matchService)
{
_matchService = matchService;
}
public virtual int DoSomething()
{
return _matchService.Add(1, 2);
}
}
public interface IMatchService
{
int Add(int a, int b);
}
public class MatchService : IMatchService
{
public virtual int Add(int a, int b)
{
return a + b;
}
}
It's always possible to get the Mock by calling the Mock.Get
method. For conveniance for each dependency, I create two properties like MatchService
and MockedMatchService
.
public class MyTests
{
protected IMatchService MatchService { get; set; }
protected Mock<IMatchService> MockedMatchService => Mock.Get(MatchService);
private IServiceProvider ServicesProvider { get; set; }
[SetUp]
public void SetupBeforeEachTest()
{
// Configure DI container
ServiceCollection services = new ServiceCollection();
ConfigureServices(services);
ServicesProvider = services.BuildServiceProvider();
// Use DI to get instances of IMatchService
MatchService = ServicesProvider.GetService<IMatchService>();
}
// In this test I mock the Add method of the dependency (IMatchService) so that it returns a value I choose
[Test]
public void TestMethod()
{
// Prepare
var matchController = ServicesProvider.GetService<MatchController>();
int expectedResult = 5;
MockedMatchService.Setup(x => x.Add(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
// Act - This will call the real DoSomething method because the MatchController has comes from a Mock with CallBase = true
int result = matchController.DoSomething();
// Check
Assert.AreEqual(expectedResult, result);
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMatchService>();
services.AddScoped<MatchController>();
}
}
I worked over @madjack and @Kritner's answers and made my
Basic Inheritable Base Test Class for Dependency Injection
Just register your services inside of it and inherite.
public class BaseTester
{
protected IProductService _productService;
protected IEmployeeService _employeeService;
public BaseTester()
{
var services = new ServiceCollection();
services.AddTransient<IProductService, ProductService>();
services.AddTransient<IEmployeeService, EmployeeService>();
var serviceProvider = services.BuildServiceProvider();
_productService = serviceProvider.GetService<IProductService>();
_employeeService = serviceProvider.GetService<IEmployeeService>();
}
}
Why would you want to inject those in a test class? You would usually test the MatchController, for example, by using a tool like RhinoMocks to create stubs or mocks. Here's an example using that and MSTest, from which you can extrapolate:
[TestClass]
public class MatchControllerTests
{
private readonly MatchController _sut;
private readonly IMatchService _matchService;
public MatchControllerTests()
{
_matchService = MockRepository.GenerateMock<IMatchService>();
_sut = new ProductController(_matchService);
}
[TestMethod]
public void DoSomething_WithCertainParameters_ShouldDoSomething()
{
_matchService
.Expect(x => x.GetMatches(Arg<string>.Is.Anything))
.Return(new []{new Match()});
_sut.DoSomething();
_matchService.AssertWasCalled(x => x.GetMatches(Arg<string>.Is.Anything);
}
Improved solution
I improved madjack's solution by wrapping it in single abstract class
and adding four methods (including two async
equivalents) with callbacks as parameters. GetRequiredScopedService<TSvc>()
is using private static
property services
for caching now, so derived classes don't create new instances over and over. Another optimization is making host
static
, so we don't build it every time in derived classes. I also removed pointless try/catch:
public abstract class TestWithDependencyInjection
{
private static readonly IHost host =
Program.CreateHostBuilder(Constants.CommandArgs).Build();
private static readonly IList<object> services =
new List<object>();
private IServiceScope svcScope;
protected async Task<TResult> UseSvcAsync<TSvc, TResult>(
Func<TSvc, Task<TResult>> callback,
bool shouldBeDisposed = true)
{
var scopedSvc = GetRequiredScopedService<TSvc>();
TResult result = await callback(scopedSvc);
if(shouldBeDisposed)
svcScope.Dispose();
return result;
}
protected async Task UseSvcAsync<TSvc>(
Func<TSvc, Task> callback)
{
var scopedSvc = GetRequiredScopedService<TSvc>();
await callback(scopedSvc);
svcScope.Dispose();
}
protected TResult UseSvc<TSvc, TResult>(
Func<TSvc, TResult> callback, bool shouldBeDisposed = true)
{
var scopedSvc = GetRequiredScopedService<TSvc>();
TResult result = callback(scopedSvc);
if(shouldBeDisposed)
svcScope.Dispose();
return result;
}
protected void UseSvc<TSvc>(Action<TSvc> callback)
{
var scopedSvc = GetRequiredScopedService<TSvc>();
callback(scopedSvc);
svcScope.Dispose();
}
private TSvc GetRequiredScopedService<TSvc>()
{
var requiredScopedSvc = (TSvc)services.SingleOrDefault(
svc => svc is TSvc);
if (requiredScopedSvc != null)
return requiredScopedSvc;
svcScope = host.Services.CreateScope();
requiredScopedSvc = svcScope.ServiceProvider
.GetRequiredService<TSvc>();
services.Add(requiredScopedSvc);
return requiredScopedSvc;
}
}
Example of returning async result from used injected service:
int foobarsCount = await UseSvcAsync<IFoobarSvc, int>(
foobarSvc => foobarSvc.GetCountAsync());
Additional information
I added optional shouldBeDisposed
argument set on true
to methods returning TResult
and Task<TResult>
in case, when you want to use same instance of service outside of callback's body:
IFoobarSvc foobarSvc = UseSvc<IFoobarSvc, IFoobarSvc>(
foobarSvc => foobarSvc, false);
Success story sharing
GetService<>
has some overloads that can be found withusing Microsoft.Extensions.DependencyInjection