I'm following this tutorial
Integration Testing with Entity Framework Core and SQL Server
My code looks like this
Integration Test Class
public class ControllerRequestsShould : IDisposable { private readonly TestServer _server; private readonly HttpClient _client; private readonly YourContext _context; public ControllerRequestsShould() { // Arrange var serviceProvider = new ServiceCollection() .AddEntityFrameworkSqlServer() .BuildServiceProvider(); var builder = new DbContextOptionsBuilder<YourContext>(); builder.UseSqlServer($"Server=(localdb)\\mssqllocaldb;Database=your_db_{Guid.NewGuid()};Trusted_Connection=True;MultipleActiveResultSets=true") .UseInternalServiceProvider(serviceProvider); _context = new YourContext(builder.Options); _context.Database.Migrate(); _server = new TestServer(new WebHostBuilder() .UseStartup<Startup>() .UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"))); _client = _server.CreateClient(); } [Fact] public async Task ReturnListOfObjectDtos() { // Arrange database data _context.ObjectDbSet.Add(new ObjectEntity{ Id = 1, Code = "PTF0001", Name = "Portfolio One" }); _context.ObjectDbSet.Add(new ObjectEntity{ Id = 2, Code = "PTF0002", Name = "Portfolio Two" }); // Act var response = await _client.GetAsync("/api/route"); response.EnsureSuccessStatusCode(); // Assert var result = Assert.IsType<OkResult>(response); } public void Dispose() { _context.Dispose(); }
As I understand it, the .UseStartUp
method ensures the TestServer
uses my startup class
The issue I'm having is that when my Act statement is hit
var response = await _client.GetAsync("/api/route");
I get an error in my startup class that the connection string is null. I think My understanding of the problem is that when my controller is hit from the client it injects my data repository, which in turn injects the db context.
I think I need to configure the service as part of the new WebHostBuilder
section so that it used the context created in the test. But I'm not sure how to do this.
ConfigureServices method in Startup.cs
public void ConfigureServices(IServiceCollection services) { // Add framework services services.AddMvc(setupAction => { setupAction.ReturnHttpNotAcceptable = true; setupAction.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); setupAction.InputFormatters.Add(new XmlDataContractSerializerInputFormatter()); }); // Db context configuration var connectionString = Configuration["ConnectionStrings:YourConnectionString"]; services.AddDbContext<YourContext>(options => options.UseSqlServer(connectionString)); // Register services for dependency injection services.AddScoped<IYourRepository, YourRepository>(); }
Thank you. Integration tests ensure that an app's components function correctly at a level that includes the app's supporting infrastructure, such as the database, file system, and network. ASP.NET Core supports integration tests using a unit test framework with a test web host and an in-memory test server.
It also has first class support for dependency injection and testing, and benefits from a special treatment by most cloud providers such as Microsoft Azure and Amazon AWS. In this article, I will show you how to fully test your ASP.NET Core Web API and discuss best practices that you can use to keep your code and tests clean.
ASP.NET Core and EF Core are a perfect match to accelerate your web app development. We'll look at using EF Core with ASP.NET Core's Dependency Injection container. Useful Links: .NET , Learn .NET, . NET Community , .NET Documentation
ASP.NET Core is a cross-platform, high-performance and open-source framework for building modern web applications. Among the many features supported by the framework, ASP.NET Core can be used to build fast and secure Web APIs.
@ilya-chumakov's answer is awesome. I just would like to add one more option
3. Use ConfigureTestServices method from WebHostBuilderExtensions.
The method ConfigureTestServices is available in the Microsoft.AspNetCore.TestHost version 2.1(on 20.05.2018 it is RC1-final). And it lets us override existing registrations with mocks.
The code:
_server = new TestServer(new WebHostBuilder() .UseStartup<Startup>() .ConfigureTestServices(services => { services.AddTransient<IFooService, MockService>(); }) );
Here are two options:
WebHostBuilder.ConfigureServices
Use WebHostBuilder.ConfigureServices
together with WebHostBuilder.UseStartup<T>
to override and mock a web application`s DI registrations:
_server = new TestServer(new WebHostBuilder() .ConfigureServices(services => { services.AddScoped<IFooService, MockService>(); }) .UseStartup<Startup>() ); public class Startup { public void ConfigureServices(IServiceCollection services) { //use TryAdd to support mocking IFooService services.TryAddTransient<IFooService, FooService>(); } }
The key point here is to use TryAdd
methods inside the original Startup
class. Custom WebHostBuilder.ConfigureServices
is called before the original Startup
, so the mocks are registered before the original services. TryAdd
doesn't do anything if the same interface has already been registered, thus the real services will not be even touched.
More info: Running Integration Tests For ASP.NET Core Apps.
Create TestStartup
class to re-configure ASP.NET Core DI. You can inherit it from Startup
and override only needed methods:
public class TestStartup : Startup { public TestStartup(IHostingEnvironment env) : base(env) { } public override void ConfigureServices(IServiceCollection services) { //mock DbContext and any other dependencies here } }
Alternatively TestStartup
can be created from scratch to keep testing cleaner.
And specify it in UseStartup
to run the test server:
_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
This is a complete large example: Integration testing your asp .net core app with an in memory database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With