3

I have setup a basic Web API project to test writing integration tests, with the below setup

Startup:

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(WebApiTest.Startup))]

namespace WebApiTest
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            WebApiConfig.Register(new System.Web.Http.HttpConfiguration());
        }
    }
}

And the following controller:

using System.Web.Http;

namespace WebApiTest.Controllers
{
    public class TestController : ApiController
    {
        public IHttpActionResult Post()
        {
            return Ok("Hello, world!");
        }
    }
}

For my Tests project I am using NUnit for my testing, and have two classes: an abstract BaseTest class, and a ControllerTests class which contains one test with the intention of sending a web request out to the above TestController's GET Action method:

BaseTest:

using Microsoft.Owin.Testing;
using NUnit.Framework;
using System;
using System.Net.Http;

namespace WebApiTest.Tests
{
    public abstract class BaseTest : IDisposable
    {
        protected const string _url = "http://localhost/";
        protected TestServer _server;

        public void Dispose()
        {
            _server.Dispose();
        }

        [SetUp]
        public void SetUp()
        {
            _server = TestServer.Create<Startup>();
        }

        protected HttpRequestMessage CreateRequest(string url, string 
contentType, HttpMethod method)
        {
            HttpRequestMessage request = new HttpRequestMessage
            {
                RequestUri = new Uri(_url + url),
                Method = method
            };
            request.Headers.Accept.Add(new 
System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(contentType));
            return request;
        }
    }
}

ControllerTests:

using FluentAssertions;
using NUnit.Framework;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace WebApiTest.Tests
{
    [TestFixture]
    public class ControllerTests : BaseTest
    {
        [Test]
        public async Task Test1()
        {
            HttpRequestMessage request = CreateRequest("test", "application/json", HttpMethod.Post);
            HttpResponseMessage response = await _server.HttpClient.SendAsync(request);
            response.StatusCode.Should().Be(HttpStatusCode.OK);
        }
    }
}

The problem I am running into is that I'm not able to reach my endpoint with the call await _server.HttpClient.SendAsync(request); as I always get a 404: Not Found response.

I'm not entirely sure what I'm doing wrong, whether it be something that's not being setup property or Api routes that aren't being mapped.

update: Here is my WebApiConfig.Register() method for reference:

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}
Delfino
  • 967
  • 4
  • 21
  • 46

2 Answers2

2

The web API was not configured correctly so the routes are not known to it

If you are self-hosting with OWIN, create a new HttpConfiguration instance. Perform any configuration on this instance, and then pass the instance to the Owin.UseWebApi extension method.

public class Startup {
    public void Configuration(IAppBuilder app) {
        var config = new System.Web.Http.HttpConfiguration();
        WebApiConfig.Register(config);
        app.UseWebApi(config); //<-- THIS WAS MISSING 
    }
}

Reference Configuring Web API with OWIN Self-Hosting

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Is this supposed to go inside my Tests project, or my actual API project? – Delfino Jun 27 '19 at 13:12
  • In your `Startup` class – Nkosi Jun 27 '19 at 13:13
  • After adding that line to my `Startup` class I tried posting to one of my controllers and got this error from Postman: `An error occurred when trying to create a controller of type 'Controller'. Make sure that the controller has a parameterless public constructor.` – Delfino Jun 27 '19 at 13:48
  • That is usually associated with an issue when creating the controller. Are you using DI with your controller? – Nkosi Jun 27 '19 at 13:51
  • Yeah, I'm using `Ninject` to inject a `IUnitOfWork` and `IMapper` into my controller, for mapping DTOs and writing records to a database. – Delfino Jun 27 '19 at 13:54
  • Then most likely one of the dependencies is not being injected correctly, causing the controller to fail when being created. recheck your startup and how you setup your API – Nkosi Jun 27 '19 at 13:57
  • I'm confused as to why `app.UseWebApi(config)` would cause some issue with DI, though. – Delfino Jun 27 '19 at 14:27
  • Can you show your `WebApiConfig.Register` and how you setup the http configuration – Nkosi Jun 27 '19 at 15:15
  • I appended the code for the `WebApiConfig.Register` method to my question. – Delfino Jun 27 '19 at 15:46
  • Not seeing where you are using DI anywhere – Nkosi Jun 27 '19 at 15:48
  • I have a `NinjectWebCommon` class that has a `Start` method which gets executed as soon as the application starts, and that is responsible for binding. Not sure if that would affect anything. – Delfino Jun 27 '19 at 15:51
  • yes it would affect things since it is not being called in the test. The TestServer know nothing about it. it is most likely using global configuration. – Nkosi Jun 27 '19 at 15:53
1

It appears that my Tests project was not aware of all the DI my API was doing at startup, since all the code for that was not contained within my Startup's Configuration method.

In a older version of my code I had the below two lines in my Configuration method:

app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(configuration);

Back when I deleted these two lines out of the Configuration method I did not understand why they were there. If the dependencies are bound at startup, it would seem that these two lines are just redundant.

While that is true, in the case of referencing this Startup class from my Tests project, I need both of those so my Test project can handle dependencies correctly.

Delfino
  • 967
  • 4
  • 21
  • 46