Quantcast
Channel: Ugo Lattanzi
Viewing all articles
Browse latest Browse all 8

An easy way to write an integration test with Web API

$
0
0

In my previous post I wrote about the Self-Host in Web API and why this features is so cool. In this I’d like to show how easy is to create an integration test with Web API (with my base class of course :)).

Before to proceed is important to understand because it’s important to create an integration test. I think this is a good point and the answer is not so easy but I’ll try to explain my point of view.

In most environments (including ASP.NET MVC) integration tests are expensive because you need a webserver, configure it for each test and so on.

With Web API you don’t have this issue, there is the Self Host that helps you. A good point for the integration test is that you can replicate a real situation and, for the Web API, you can test the content negotiation, authentication and other important features.

The recipe for this test includes:

  • xUnit (is not mandatory but actually it’s my favorite test framework);
  • SharpTestEx (is an incredible cool library wrote by my friend Fabio Maulo);
The problem of the integration test is the repetitiveness code, in fact you have to create the server, configure it, make a request and so on.

For this reason I created a base class that helps us for this job and the result is pretty awesome. In this example I want to test the Action below:

publicclassValuesController:ApiController{publicIEnumerable<string>Get(){returnnew[]{"http://tostring.it","http://imperugo.tostring.it","http://twitter.com/imperugo","http://www.linkedin.com/in/imperugo"};}}

And the unit test should be like that:

[Fact]publicvoidValueController_WithGetMethos_ShouldReturnValidData(){HttpSelfHostConfigurationconfiguration=newHttpSelfHostConfiguration("http://localhost:8080");configuration.IncludeErrorDetailPolicy=IncludeErrorDetailPolicy.Always;configuration.Services.Replace(typeof(IAssembliesResolver),newWebApiClassBase.TestAssemblyResolver(typeof(ValuesController)));configuration.Routes.MapHttpRoute("Default","{controller}",new{controller="Home"});HttpSelfHostServerserver=newHttpSelfHostServer(configuration);try{server.OpenAsync().Wait();varrequest=newHttpRequestMessage();request.RequestUri=newUri("http://localhost:8080");request.Method=HttpMethod.Get;varclient=newHttpClient(server);using(HttpResponseMessageresponse=client.SendAsync(request).Result){response.Should().Not.Be.Null();response.IsSuccessStatusCode.Should().Be.True();string[]result=response.Content.ReadAsAsync<string[]>().Result;result.Length.Should().Be.EqualTo(4);result[0].Should().Be.EqualTo("http://tostring.it");result[1].Should().Be.EqualTo("http://imperugo.tostring.it");result[2].Should().Be.EqualTo("http://twitter.com/imperugo");result[3].Should().Be.EqualTo("http://www.linkedin.com/in/imperugo");}}finally{configuration.Dispose();server.Dispose();}}

This code has several problems; the code is not so easy to read, not maintainable, it’s repetitive, etc. I wrote a base class that reduce and improve the test dramatically and the result is this:

publicclassValuesControllerTest:WebApiClassBase{publicValuesControllerTest():base("localhost",8080,typeof(ValuesController)){}  [Fact]publicvoidValueController_WithGetMethos_ShouldReturnValidData(){base.Start();varresponse=base.CreateRequest("/Values",HttpMethod.Get);response.Should().Not.Be.Null();response.IsSuccessStatusCode.Should().Be.True();string[]result=response.Content.ReadAsAsync<string[]>().Result;result.Length.Should().Be.EqualTo(4);result[0].Should().Be.EqualTo("http://tostring.it");result[1].Should().Be.EqualTo("http://imperugo.tostring.it");result[2].Should().Be.EqualTo("http://twitter.com/imperugo");result[3].Should().Be.EqualTo("http://www.linkedin.com/in/imperugo");}}

I replicated the same test but in a more elegant way. The only important things to remember are in the constructors: The first parameter is the host, the second one the port and the last one is the type of the controller to test.

Obviously the class is not complete and more features will come, but now you can override the configuration so you can configure the Routing and other important parameters. If you like this approach and you want to use or extend my base class, you’re welcome to do it here!


Viewing all articles
Browse latest Browse all 8

Trending Articles