【问题标题】:Unit testing WebApi controllers in WebApi在 Web Api 中对 Web Api 控制器进行单元测试
【发布时间】:2014-10-17 16:29:55
【问题描述】:

我正在尝试对我的控制器进行单元测试,但是一旦此控制器使用其嵌入的 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方法就是这个,见here

    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 的一个非常有据可查的问题,您可以阅读更多关于它的信息 here 例如(“测试链接生成”)。基本上,它归结为设置自定义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。所以我不明白为什么它不起作用,最重要的是,我不明白如何让它起作用。

谢谢!

【问题讨论】:

  • 您希望Link 方法在您的测试中返回什么?
  • 其实我并不在意。

标签: c# unit-testing asp.net-web-api


【解决方案1】:

不确定您链接到的文档自您的原始帖子以来是否已更新,但它们显示了一个示例,其中模拟了 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);
}

【讨论】:

    【解决方案2】:

    所以,你需要模拟 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);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-11
      • 2021-10-15
      • 1970-01-01
      相关资源
      最近更新 更多