Steffen
Steffen

Reputation: 2431

Automatic testing of ApiController

I have an ApiController and would like to test it with unit tests including the routing.

An example:

[RoutePrefix("prefix")]
public class Controller : ApiController
{
    [HttpGet]
    [Route("{id1}")]
    public int Add(int id1, [FromUri] int id2)
    {
        return id1 + id2;
    }
}

I would now like to test this method. I see, that I can test it like an ordinary method. But I would also like to test it with the translation of the URL to the method parameters.

Basically I would like to have an automatic test where I call a URL like prefix/10?id2=5 and get a result of 15. Is this somehow possible?

Upvotes: 2

Views: 609

Answers (4)

Andriy Tolstoy
Andriy Tolstoy

Reputation: 6090

  1. Create an OWIN StartUp class using Microsoft ASP.NET Web API 2.2 OWIN package:

    public class Startup
    { 
        public void Configuration(IAppBuilder builder)
        {
            var config = new HttpConfiguration();
    
            builder.UseWebApi(config);
    
            config.MapHttpAttributeRoutes();
            config.EnsureInitialized();
        }
    }
    
  2. Use Microsoft ASP.NET Web API 2.2 Self Host package in your tests (used NUnit for example):

    [Test]
    [TestCase(10, 5, 15)]
    [TestCase(1, 2, 3)]
    // add your test cases
    public async Task AdditionTests(int a, int b, int result) 
    {
        // Arrange
        var address = "http://localhost:5050";
    
        using (WebApp.Start<Startup>(address))
        {
            var client = new HttpClient();
    
            var requestUri = $"{address}/prefix/{a}?id2={b}";
    
            // Act
            var response = await client.GetAsync(requestUri);
    
            // Assert
            Assert.IsTrue(await response.Content.ReadAsAsync<int>() == result);
        }
    }
    

Upvotes: 2

Nkosi
Nkosi

Reputation: 247213

I wrote a little helper class for in-memory integration testing that can be called as part of the test suit.

internal interface IHttpTestServer : IDisposable {
    HttpConfiguration Configuration { get; }
    HttpClient CreateClient();
}

internal class HttpTestServer : IHttpTestServer {
    HttpServer httpServer;

    public HttpTestServer(HttpConfiguration configuration = null) {
        httpServer = new HttpServer(configuration ?? new HttpConfiguration());
    }

    public HttpConfiguration Configuration {
        get { return httpServer.Configuration; }
    }

    public HttpClient CreateClient() {
        var client = new HttpClient(httpServer);
        return client;
    }

    public void Dispose() {
        if (httpServer != null) {
            httpServer.Dispose();
            httpServer = null;
        }
    }

    public static IHttpTestServer Create(HttpConfiguration configuration = null) {
        return new HttpTestServer(configuration);
    }
}

And would then use it like this

[TestMethod]
public async Task HttpClient_Should_Get_OKStatus_From_InMemory_Hosting() {

    using (var server = new HttpTestServer()) {

        MyWebAPiProjectNamespace.WebApiConfig.Configure(server.Configuration);

        var client = server.CreateClient();

        string url = "http://localhost/prefix/10?id2=5";
        var expected = 15;

        var request = new HttpRequestMessage {
            RequestUri = new Uri(url),
            Method = HttpMethod.Get
        };

        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        using (var response = await client.SendAsync(request)) {
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            var result = await response.Content.ReadAsAsync<int>();
            Assert.AreEqual(expected, result);
        }
    }
}

This will configure an in-memory test server that the test can make calls to using its httpclient. It is in essence an end-to-end integration test.

Upvotes: 3

Satheshkumar
Satheshkumar

Reputation: 232

Its possible by using postman or fiddler to test with url param..

Upvotes: 1

Igor
Igor

Reputation: 62228

That is an integration test, not a unit test. If you wanted to automate this you would have to have a tool that would launch/host your web api and then execute requests against it.

If you wanted to keep it as a unit test though you could validate the attributes on the class and the method and check the values.

var type = typeof(Controller);
var attributeRoutePrefix = type.GetCustomAttribute(typeof(RoutePrefixAttribute)) as RoutePrefixAttribute;
Assert.IsNotNull(attributeRoutePrefix);
Assert.AreEqual("prefix", attributeRoutePrefix.Prefix);

var methodAttribute = type.GetMethod(nameof(Controller.Add)).GetCustomAttribute(typeof(RouteAttribute)) as RouteAttribute;
Assert.IsNotNull(methodAttribute);
Assert.AreEqual("id1", methodAttribute.Template);

Upvotes: 1

Related Questions