dknaack
dknaack

Reputation: 60496

Unit Testing Async Controller: Exception on Url.Link()

I would like to unit test a async controller. Ive done this many times before but never called the Url.Link()method in order to generate the Location Header.

Heres my demo code.

Controller

public class DemoController : ApiController
{
    [HttpPost]
    public async Task<IHttpActionResult> DemoRequestPost(string someRequest)
    {
        // do something await ...
        var id = 1;
        // generate url for location header (here the problem occurrs)
        var url = Url.Link("DemoRequestGet", new {id = id});
        return Created(url, id);
    }

    [HttpGet]
    [Route("demo/{id}", Name = "DemoRequestGet")]
    public async Task<IHttpActionResult> DemoRequestGet(int id)
    {
        // return something
        return Ok();
    }
}

Test

[TestFixture]
public class DemoControllerTests
{
    [Test]
    public async Task CreateFromDraftShouldSucceed()
    {
        // Arrange
        var request = "Hello World";
        var controller = new DemoController();
        var httpConfiguration = new HttpConfiguration();
        // ensure attribte routing is setup
        httpConfiguration.MapHttpAttributeRoutes();
        httpConfiguration.EnsureInitialized();
        controller.Configuration = httpConfiguration;
        // Act
        var result = await controller.DemoRequestPost(request);
        // Assert
        Assert.AreEqual(result, 1);
    }
}

I am receiving

at NUnit.Framework.Internal.ExceptionHelper.Rethrow(Exception exception) at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult) at NUnit.Framework.Internal.Commands.TestMethodCommand.RunAsyncTestMethod(TestExecutionContext context)

Upvotes: 1

Views: 601

Answers (2)

alltej
alltej

Reputation: 7285

@dknaack For your controller test, you probably don't need this line of code:

    var httpConfiguration = new HttpConfiguration();
    httpConfiguration.MapHttpAttributeRoutes();
    httpConfiguration.EnsureInitialized();
    controller.Configuration = httpConfiguration;

Upvotes: 0

Nkosi
Nkosi

Reputation: 247163

Check the Testing Link Generation in Unit Testing Controllers in ASP.NET Web API 2

The UrlHelper class needs the request URL and route data, so the test has to set values for these.

You are not setting those in your example hence your error.

Here is one of their examples

[TestMethod]
public void PostSetsLocationHeader()
{
    // Arrange
    ProductsController controller = new ProductsController(repository);

    controller.Request = new HttpRequestMessage { 
        RequestUri = new Uri("http://localhost/api/products") 
    };
    controller.Configuration = new HttpConfiguration();
    controller.Configuration.Routes.MapHttpRoute(
        name: "DefaultApi", 
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional });

    controller.RequestContext.RouteData = new HttpRouteData(
        route: new HttpRoute(),
        values: new HttpRouteValueDictionary { { "controller", "products" } });

    // Act
    Product product = new Product() { Id = 42, Name = "Product1" };
    var response = controller.Post(product);

    // Assert
    Assert.AreEqual("http://localhost/api/products/42", response.Headers.Location.AbsoluteUri);
}

based on that article you can do something like this which is mocking the UrlHelper using Moq.

[TestClass]
public class DemoControllerTests {
    [TestMethod]
    public async Task CreateFromDraftShouldSucceed() {
        // This version uses a mock UrlHelper.

        // Arrange
        var controller = new DemoController();
        controller.Request = new HttpRequestMessage();
        controller.Configuration = new HttpConfiguration();

        string locationUrl = "http://localhost/api/demo/1";

        // Create the mock and set up the Link method, which is used to create the Location header.
        // The mock version returns a fixed string.
        var mockUrlHelper = new Mock<UrlHelper>();
        mockUrlHelper.Setup(x => x.Link(It.IsAny<string>(), It.IsAny<object>())).Returns(locationUrl);
        controller.Url = mockUrlHelper.Object;

        // Act
        var request = "Hello World";
        var result = await controller.DemoRequestPost(request);
        var response = await result.ExecuteAsync(System.Threading.CancellationToken.None);

        // Assert
        Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
    }
}

Upvotes: 1

Related Questions