An issue I’ve had while developing integration tests in .NET Core is sharing information between my TestContext and the Startup class.
The documented approach looks something like this:
var hostBuilder = new WebHostBuilder().UseStartup() _server = new TestServer(hostBuilder);
The problem is that Startup
is called from deep within new TestServer
making it impossible to pass a reference from the calling context. This is particularly a problem with integration tests on an API, where we need the an HttpClient
to be made from the TestServer
instance in order to call API methods.
_client = _server.CreateClient();
Dependency Injection into Startup
What I hadn’t originally appreciated is that Startup
class accepts dependencies defined by the host. Therefore anything already configured in the services, which is the container for ASP.NET’s dependency injection system, is available for injection into Startup
.
For instance, to pass a reference to the current TestContext
we register the current instance as a singleton before calling UseStartup
:
var hostBuilder = new WebHostBuilder() .ConfigureServices(s => { s.AddSingleton(this); }) .UseStartup()
Now, a the TestContext
in the following Startup
class will be populated:
public class Startup { private TestContext _ctx; public Startup(IConfiguration config, TestContext ctx) { _ctx = ctx; } ...
Passing a Shared Object
A more cohesive approach is to place mutual dependencies in another class and make it available via much the same approach. The following is an example allowing any class access to the TestServer’s client.
public interface ITestDependencies { public TestContext Context {get;} // also various Mock objects... } public class TestDependencies : ITestDependencies { public TestContext Context {get; private set;} public TestDependencies(TestContext ctx) { Context = ctx; } } public class Startup { private readonly ITestDependencies _testDependencies; public Startup(IConfiguration configuration, ITestDependencies testDependencies) { _testDependencies = testDependencies; } // other methods - use _testDependencies.Context.Client } public class TestContext { public HttpClient Client {get; private set;} private readonly TestServer _server; public TestContext() { var builder = new WebHostBuilder() .ConfigureServices((IServiceCollection services) => { services.AddSingleton(typeof(ITestDependencies), new TestDependencies(this)); }) .UseStartup(); _server = new TestServer(builder); Client = _server.CreateClient(); } }