A very popular post about integration testing ASP.NET Web API was published quite some time ago. However, since then, OWIN has been released. OWIN makes integration testing ASP.NET Web API much simpler. This post describes what is required to set an OWIN-based integration testing framework up.
This, believe it or not, only requires a single line of code with OWIN self-hosting! It assumes that your web API project is powered by ASP.NET Web API 2.2 with OWIN.
The basic idea behind this particular approach is that the integration test project self-hosts the web API project. Consequently, the integration tests are executed against the self-hosted web app. In order to achieve this, the web API project must be modified a little bit. Fortunately, this is easy with OWIN.
A web app powered by OWIN has two entry points: Application_Start() and Startup.Configuration(IAppBuilder). There isn’t a big difference between the entry points but it is important to know that Startup.Configuration is invoked a bit later than Application_Start().
So the integration test project can self-host the web API project, all web API initialization logic must be moved to Startup.Configuration(IAppBuilder) as follows:
[sourcecode language=”csharp” gutter=”false” autolinks=”true” toolbar=”true”]
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration();
WebApiConfig.Register(configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// Execute any other ASP.NET Web API-related initialization, i.e. IoC, authentication, logging, mapping, DB, etc.
ConfigureAuthPipeline(app);
app.UseWebApi(configuration);
}
}
[/sourcecode]
Not much is left in the old Application_Start() method:
[sourcecode language=”csharp” gutter=”false” autolinks=”true” toolbar=”true”]
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
[/sourcecode]
Now, in order to test your web API project, add the following OWIN NuGet packages to your tests project:
[sourcecode language=”powershell” gutter=”false”]
PM> Install-Package Microsoft.Owin.Hosting
PM> Install-Package Microsoft.Owin.Host.HttpListener
[/sourcecode]
Following is the single line of code that is required to self-host your web API project:
[sourcecode language=”csharp” gutter=”false”]
using (var webApp = WebApp.Start<Startup>("http://*:9443/"))
{
// Execute test against the web API.
webApp.Dispose();
}
[/sourcecode]
That single line starts the web API up.
It is better that this is set up once for all integration tests for your tests project using MSTest’s AssemblyInitialize attribute as follows:
[sourcecode language=”csharp” gutter=”false”]
[TestClass]
public class WebApiTests
{
private static IDisposable _webApp;
[AssemblyInitialize]
public static void SetUp(TestContext context)
{
_webApp = WebApp.Start<Startup>("http://*:9443/");
}
[AssemblyCleanup]
public static void TearDown()
{
_webApp.Dispose();
}
}
[/sourcecode]
Now, the test code for testing a web API, which is self-hosted at localhost:9443 and protected using token-based authentication, can be written as follows:
[sourcecode language=”csharp” gutter=”false”]
[TestMethod]
public async Task TestMethod()
{
using (var httpClient = new HttpClient())
{
var accessToken = GetAccessToken();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
var requestUri = new Uri("http://localhost:9443/api/values");
await httpClient.GetStringAsync(requestUri);
}
}
[/sourcecode]
The above approach has a wide range of advantages:
- You don’t have to deploy the web API project
- The test and web API code run in the same process
- Breakpoints can be added to the web API code.
- The entire ASP.NET pipeline can be tested with routing, filters, configuration, etc.
- Any web API dependencies that will otherwise be injected from a DI container, can be faked from the test code
- Tests are quick to run!
On the other hand, there are a few limitations, since there are different behaviours between IIS hosting and self hosting. For example, HttpContext.Current is null, when the web API is self-hosted. Any asynchronous code might also result in deadlocks when it runs in IIS while it runs fine in the self-hosted web app. There are also complexities with setting the SSL certificate for the self-hosted web app. Please note that Visual Studio should run with elevated elevated privileges.
Nevertheless, with a careful consideration, integration tests can be authored to run against a self-hosted or cloud-hosted web API.
This provides a tremendous productivity boost, given the easy ability to run integration tests in process, with a setup cost that is negligible as described in this post.
Update:
It was suggested in the comments to utilize In-Memory hosting instead of Self-Hosting solution. (See details here)
Another nuget package should be referenced: Microsoft.Owin.Testing. The resulting in-memory api call is executed as follows:
[sourcecode language=”csharp” gutter=”false”]
using (var server = TestServer.Create<Startup>())
{
// Execute test against the web API.
var result = await server.HttpClient.GetAsync("/api/values/");
}
[/sourcecode]
Unfortunately, in-memory solution doesn’t work for me out of the box. Seems like it can’t handle authentication. I use UseJwtBearerAuthentication (JWT bearer token middleware) and my api calls result in 401.
Great post and very informative, thanks Boris
Please don’t run use self hosting for integration tests using, it’s a bad practice.
There is a bunch of reasons for that but the main ones are:
a) you may run into port permission issues on the machine that executes the tests
b) imagine a CI server running plenty of tests in parallel – then they might all try to setup a self host on the same port and potentially fail as they would be interfering with each other
c) you end up relying on the machine’s networking stack, and if that is misbehaving, then your tests will fail for no reason related to your application
d) for the duration of your test, the machine running the tests exposes externally available HTTP endpoints which is hardly desirable
Instead, Katana allows you to run the entire pipeline in memory (in process) by providing an adapter between HttpClient and the OWIN pipeline in a form of a message handler. This is the same principle as used in memory hosting the Web API HttpServer alone.
There are plenty of resources about that:
– http://blogs.msdn.com/b/webdev/archive/2013/11/26/unit-testing-owin-applications-using-testserver.aspx
– http://www.strathweb.com/2013/12/owin-memory-integration-testing/
– http://dhickey.ie/post/2013/08/27/Introducing-OwinTesting.aspx
Thanks for your comment. I’ll update the blog with TestServer.Create() option. However, I can’t make it work with authentication. My OWIN pipeline contains UseJwtBearerAuthentication() and with TestServer the response is 401
Hi Boris,
I’m using JWT too (with AAD) and I was having the same 401 issue you experimented. I was able to fix it by calling the line app.UseWebApi(config) last (this is, after app.UseWindowsAzureActiveDirectoryBearerAuthentication)
_server = TestServer.Create(app =>
{
var config = new HttpConfiguration();
config.Services.Replace(typeof(IAssembliesResolver), new TestWebApiResolver());
var apiConfig = ApiConfiguration.GetCurrentConfiguration();
AutoFacConfig.Configure(apiConfig, config);
WebApiConfig.Register(config);
var parameters = new System.IdentityModel.Tokens.TokenValidationParameters();
parameters.ValidAudience = apiConfig.Audience;
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
//Audience = apiConfig.Audience,
TokenValidationParameters = parameters,
Tenant = apiConfig.Tenant
}
);
//This should be called last.
app.UseWebApi(config);
});
This post helped me a lot configuring the self-hosted server http://www.juliencorioland.net/archives/using-owin-to-test-your-web-api-controllers#.VbEuzflVhBd
The code is on github too https://github.com/simonferquel/techdays-paris-2014-mvc-webapi
Yes I went for the test server way also https://www.nuget.org/packages/Microsoft.Owin.Testing/. This runs it all In memory and the CI problems disappear.
I actually wrote a similar blog only the other day using the nuget.
http://tomharrisnet.blogspot.co.uk/2014/10/testing-webapi-with-owin-hosting.html?m=1
Only down side of this so far is parallel running test, seems to mess up on occasion.
@tomharrisnet
Good Article!
Hi,
I have following your post and have got an integration test which starts a web app working when it runs within Visual Studio.
Unfortunately I have no such joy when I get our TeamCity CI server to run the test. In this case, I get the error:
System.MissingMemberException: The server factory could not be located for the given input: Microsoft.Owin.Host.HttpListener
All the tests run one at a time, so it’s nothing to do with the parallel test running issues. If anyone has any ideas why I might have this problem, I’d love to hear them!
Hi Simon – recommend taking a look at this Stack Overflow post solution to see if it helps you out.
“Unfortunately, in-memory solution doesn’t work for me out of the box. Seems like it can’t handle authentication. I use UseJwtBearerAuthentication (JWT bearer token middleware) and my api calls result in 401.”
i faced the same, but if i do some thing like this.
/*get token*/
InitializeServer();
string token = GetToken();
TearDown();
/*restart server again and test auth*/
Setup();
InitializeServer();
[Authorize] attr works
i will update my codes at https://github.com/DiponRoy/WebApiTest
but for me the authProvider was n’t working as expected, which forced me to use controller to get token
Is it possible to use OWIN for testing a ASP.NET Core project?
Sure. For ASP.NET Core you could use TestServer from Microsoft.AspNetCore.TestHost package.
For example:
var server = new TestServer(new WebHostBuilder().UseUrls(“https://localhost:9001”).UseStartup().UseEnvironment(“Development”))
{
BaseAddress = new Uri(“https://localhost:9001”)
};
var httpClient = server.CreateClient().AcceptJson();
TestServer works nicely for testing the API, but:
I’m running an Angular2 app from my ASP.NET Core backend, and for Angular2 End2End tests I can’t use testserver.CreateClient() since it does not understand Angular.
I need to use a tool like Protractor, and for this the web page needs to be accessible during testing by using it’s URL in a browser. The Testserver does not really run a server, so no access for Protractor.
TestServer with jwt tokent returns 401 invalid signature. but if i call api directly with same token using postman it works. Is TestServer capable of handling authentications with jwt token in header ?