Search code examples
asp.net-web-api2integration-testingowinkatana

HttpClient default headers not working with Microsoft.Owin.Testing.TestServer


I'm using the Microsoft.Owin.Testing library to integration test my API in-memory. I've added in the OWIN JWT middleware for my authentication needs, and am now trying to pass a generated token to test requests to controllers needing authorization. I can assure you that the JWT middleware is setup correctly, as it works just fine with normal use. However, I am observing some strange behavior with the TestServer.HttpClient object. When I set a default authorization header on HttpClient to pass the token, my tests never pass because the token is not recognized. However, when I use TestServer.CreateRequest(...), the test passes correctly and the token is recognized. I would prefer to use the HttpClient methods because they make things a hell of a lot easier with all the extension methods provided such as PostAsJsonAsync, etc. I'm beginning to think there is either a bug in the TestServer.HttpClient or I that am completely missing something.

Here's my test class (using NUnit3):

public class DefinitionsControllerTests
{
    private TestServer _server;
    private string _accessToken;

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

        var credentials = new FormUrlEncodedContent(new[] {
            new KeyValuePair<string, string>("grant_type", "password"),
            new KeyValuePair<string, string>("username", "john.doe@mail.com"),
            new KeyValuePair<string, string>("password", "testing123")
        });

        // get token from OWIN JWT middleware
        dynamic resultBody = JObject.Parse(
            _server.HttpClient.PostAsync("/oauth/token", credentials).Result.Content.ReadAsStringAsync().Result);

        _accessToken = (string)resultBody.access_token;

        // this does not appear to ever work
        _server.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
    }

    [TearDown]
    public void TearDown()
    {
        _server.Dispose();
    }

    [Test]
    public void GetById_WithExistingId()
    {
        // 401 Unauthorized response everytime and test fails
        var response = _server.HttpClient.GetAsync($"/api/definitions/{expected.Id}").Result;
        var actual = response.Content.ReadAsAsync<Definition>().Result;

        // 200 Ok every time and test passes
        // - these variables aren't part of the test but rather to show alternate request creation method that works
        var response2 = _server.CreateRequest($"/api/definitions/{expected.Id}")
            .AddHeader("Authorization", "Bearer " + _accessToken)
            .GetAsync()
            .Result;
        var actual2 = response.Content.ReadAsAsync<Definition>().Result;

        response.StatusCode.ShouldBe(HttpStatusCode.OK);
        actual.ShouldNotBeNull();
    }

    //...other test methods
}

And my controller:

[Authorize]
public class DefinitionsController : ApiController
{
    private readonly IDefinitionRepository _repo;

    public DefinitionsController(IDefinitionRepository repo)
    {
        _repo = repo;
    }

    public IHttpActionResult Get(Guid id)
    {
        var definition = _repo.Get(id);

        if (definition == null)
            return NotFound();

        return Ok(definition);
    }
}

Anyone have any idea why only CreateRequest() works? This is slightly infuriating.


Solution

  • The problem is that the HttpClient property returns a new instance every time. It will work if you save that instance and re-use it. https://github.com/aspnet/AspNetKatana/blob/b850cd8b4de61e65bbd7127ce02b5df7c4cb6db5/src/Microsoft.Owin.Testing/TestServer.cs#L48