在WebApi中对WebApi控制器进行单元测试

本文关键字:WebApi 单元测试 控制器 中对 | 更新日期: 2023-09-27 18:26:51

我正试图对我的控制器进行单元测试,但一旦该控制器使用其嵌入的UrlHelper对象,它就会抛出一个ArgumentNullException

我试图测试的动作是这样的:

    public HttpResponseMessage PostCommandes(Commandes commandes)
    {
        if (this.ModelState.IsValid)
        {
            this.db.AddCommande(commandes);
            HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.Created, commandes);
            // this returns null from the test project
            string link = this.Url.Link(
                "DefaultApi",
                new
                {
                    id = commandes.Commande_id
                });
            var uri = new Uri(link);
            response.Headers.Location = uri;
            return response;
        }
        else
        {
            return this.Request.CreateResponse(HttpStatusCode.BadRequest);
        }
    }

我的测试方法如下:

    [Fact]
    public void Controller_insert_stores_new_item()
    {
        // arrange
        bool isInserted = false;
        Commandes item = new Commandes() { Commande_id = 123 };
        this.fakeContainer.AddCommande = (c) =>
            {
                isInserted = true;
            };
        TestsBoostrappers.SetupControllerForTests(this.controller, ControllerName, HttpMethod.Post);
        // act
        HttpResponseMessage result = this.controller.PostCommandes(item);
        // assert
        result.IsSuccessStatusCode.Should().BeTrue("because the storage method should return a successful HTTP code");
        isInserted.Should().BeTrue("because the controller should have called the underlying storage engine");
        // cleanup
        this.fakeContainer.AddCommande = null;
    }

SetupControllerForTests方法就是这个,如图所示:

    public static void SetupControllerForTests(ApiController controller, string controllerName, HttpMethod method)
    {
        var request = new HttpRequestMessage(method, string.Format("http://localhost/api/v1/{0}", controllerName));
        var config = new HttpConfiguration();
        var route = WebApiConfig.Register(config).First();
        var routeData = new HttpRouteData(route, new HttpRouteValueDictionary
                                                 {
                                                     {
                                                             "controller",
                                                             controllerName
                                                     }
                                                 });
        controller.ControllerContext = new HttpControllerContext(config, routeData, request);
        controller.Request = request;
        controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
        controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
    }

对于WebApi2来说,这是一个记录得很好的问题,例如,您可以在这里阅读更多关于它的信息("测试链接生成")。基本上,它可以归结为设置自定义ApiController.RequestContext,或者模拟控制器的Url属性。

问题是,在我的WebApi版本(Nuget包:Microsoft.AspNet.WebApi 4.0.20710.0/WebApi.Core.4.0.30506.0)中,ApiController.RequestContext不存在,Moq不能模拟UrlHelper类,因为它应该模拟的方法(Link)是不可重写的,或者类似的东西(我没有详细讨论)。因为我正在使用WebApi 1。但是我的代码所基于的博客文章(以及许多其他文章)也使用V1。所以我不明白为什么它不起作用,最重要的是,我如何才能让它起作用。

谢谢!

在WebApi中对WebApi控制器进行单元测试

不确定您链接到的文档自原始帖子以来是否已更新,但它们显示了一个模拟UrlHelperLink方法的示例。

[TestMethod]
public void PostSetsLocationHeader_MockVersion()
{
    // This version uses a mock UrlHelper.
    // Arrange
    ProductsController controller = new ProductsController(repository);
    controller.Request = new HttpRequestMessage();
    controller.Configuration = new HttpConfiguration();
    string locationUrl = "http://location/";
    // 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
    Product product = new Product() { Id = 42 };
    var response = controller.Post(product);
    // Assert
    Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}

因此,您需要模拟UrlHelper.Link方法。使用Typemock Isolator(给定链接的测试示例)可以很容易地完成:

[TestMethod, Isolated]
public void PostSetsLocationHeader_MockVersion()
{
    // This version uses a mock UrlHelper.
    // Arrange
    ProductsController controller = new ProductsController(repository);
    controller.Request = new HttpRequestMessage();
    controller.Configuration = new HttpConfiguration();
    string locationUrl = "http://location/";
    // 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 = Isolate.Fake.Instance<UrlHelper>();
    Isolate.WhenCalled(() => mockUrlHelper.Link("", null)).WillReturn(locationUrl);
    controller.Url = mockUrlHelper;
    // Act
    Product product = new Product() { Id = 42 };
    var response = controller.Post(product);
    // Assert
    Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}