单元测试异步控制器:Url.Link() 异常

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

我想对异步控制器进行单元测试。我以前做过很多次,但从未调用 Url.Link() 方法来生成位置 Header。

这是我的演示代码。

控制器

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);
    }
}

我收到了

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)

Unit Testing Controllers in ASP.NET Web API 2

中检查测试Link生成

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

你没有在你的例子中设置那些所以你的错误。

这是他们的例子之一

[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);
}

根据那篇文章,您可以使用 Moq 来模拟 UrlHelper

[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);
    }
}

@dknaack 对于您的控制器测试,您可能不需要这行代码:

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