Reputation: 60496
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.
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();
}
}
[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
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
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