Reputation: 69
I'm writing a unit test for my web API (everything is implemented using .Net Core 2.2). For this test, I have to actually send a request to the web API. However, I can't really figure out how to start the API by code. It seems you can't start the web service by code.
So I tried a workaround using scripts. Which worked fine on Windows however Linux caused some problems.
dotnet run --project <pathToProject>
would normally start the API. However, after executing the script everything terminated instantly. The created C# process instance had ExitCode = 0
and HasExited = true
.
Has anyone encountered such a problem? Or any ideas?
Thanks in advance
Upvotes: 0
Views: 258
Reputation: 186
If I understand your question correctly, there is a very easy way to do that. In the Microsoft.AspNetCore.TestHost
nampespace there is a TestServer
class that easily allows you to fire up a test server for your API. However, there are some things that you need to pay attention, like what Configuration to load, what services and so on. The background for doing this is that you might want to mock some services like DB repositories when you test and use instead in memory collections or whatever might be suited for your tests.
Here is a basic implementation. First of all, in the API that you want to test, make sure that the services that need to be mocked are registered via virtual methods in the Startup
class.
Example of a stripped off ConfigureServices
mehtod:
public void ConfigureServices(IServiceCollection services)
{
RegisterDatabase(services);
}
And the implementation also in Startup.cs
(I use Mongo in this example):
protected virtual void RegisterDatabase(IServiceCollection services)
{
string mongoConnectionString = Configuration.GetConnectionString("DevCN");
services.AddScoped<IMyDbProcessor, MyDbProcessor>(sp =>
{
var mongoClient = new MongoClient(mongoConnectionString);
return new MyDbProcessor(mongoClient);
});
}
Please note that classes for data processing are specific to the API from which I took this sample.
Next in the Testing Project (doesn't matter what test runner or unit test framework you use) you create a StartUp
class where you override database registration. In this override you can register mocks for instance.
public class MyTestStartup : Startup
{
public MyTestStartup(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void RegisterDatabase(IServiceCollection services)
{
services.AddScoped<IMyDbProcessor>(db => new TestDbProcessor());
}
}
Then still in the Unit test project you can create a class to hold your test server and configure with whatever you need. Here is a sample for how this might look:
public class MyTestServer
{
//I add an HttpClient that will be used in the test methods to make the actual call
internal HttpClient Client { get; set; }
public MyTestServer()
{
IWebHostBuilder builder = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
IHostingEnvironment env = hostingContext.HostingEnvironment;
config.AddJsonFile("testsettings.json", optional: true)
.AddJsonFile($"testsettings.{env.EnvironmentName}.json", optional: true);
})
.UseStartup<MyTestStartup>();
builder.UseSetting(WebHostDefaults.ApplicationKey, typeof(Program).GetTypeInfo().Assembly.FullName);
var testServer = new TestServer(builder);
Client = testServer.CreateClient();
}
}
Here pay special attention to the typeof(Program).GetTypeInfo().Assembly.FullName)
part. "Program" should reference your Program.cs
in your API.
Then in the unit test class you can use the constructor of the class to create a new server and get the needed client to make the calls.
_server = new MyTestServer();
_client = _server.Client;
Now you can use the client to make calls to your API. Each time you run a test, there will be a test server that runs your API. Note: If you don't want to add custom configuration (so specific for your test environment) you can simplify the creation of the web server.
Upvotes: 1