简体   繁体   English

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

[英]Unit testing WebApi controllers in WebApi

I am trying to unit test my controller, but as soon as this controller uses its embedded UrlHelper object, it throws an ArgumentNullException . 我正在尝试对控制器进行单元测试,但是一旦该控制器使用其嵌入式UrlHelper对象,它将引发ArgumentNullException

The action I'm trying to test is this one: 我要测试的操作是以下操作:

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

My test method looks like this: 我的测试方法如下:

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

And the SetupControllerForTests method is this one, as seen here : 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;
    }

This is a pretty well documented problem for WebApi2, you can read more about it here for instance ("testing link generation"). 对于WebApi2,这是一个文档齐全的问题,例如,您可以在此处阅读有关问题的更多信息(“测试链接生成”)。 Basically, it boils down to either setting a custom ApiController.RequestContext , or mocking the controller's Url property. 基本上,它归结为设置自定义ApiController.RequestContextApiController.RequestContext控制器的Url属性。

The problem is that, in my version of WebApi (Nuget packages: Microsoft.AspNet.WebApi 4.0.20710.0 / WebApi.Core.4.0.30506.0), ApiController.RequestContext does not exist, and Moq cannot mock the UrlHelper class, because the method it should mock ( Link ) is not overridable, or something like that (I didn't dwell on it). 问题是,在我的WebApi版本(Nuget软件包:Microsoft.AspNet.WebApi 4.0.20710.0 / WebApi.Core.4.0.30506.0)中, ApiController.RequestContext不存在,并且Moq无法模拟UrlHelper类,因为该方法它应该模拟( Link )是不可替代的,或者类似的东西(我没有对此进行介绍)。 Because I'm using WebApi 1. But the blog post I based my code on (as well as many other posts) use V1, too. 因为我使用的是WebApi1。但是我基于我的代码的博客文章(以及许多其他文章)也使用了V1。 So I don't understand why it doesn't work, and most of all, how I can make it work. 所以我不明白为什么它不起作用,最重要的是我如何使它起作用。

Thank you ! 谢谢 !

Not sure if the documentation you linked to was updated since your original post but they show an example where they mock up the UrlHelper and also the Link method. 不确定自原始帖子以来,您链接的文档是否已更新,但它们显示了一个示例,其中模拟了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);
}

So, you need to mock UrlHelper.Link method. 因此,您需要模拟UrlHelper.Link方法。 It can be easily done with Typemock Isolator (test example from given link): 使用Typemock隔离器可以轻松完成(来自给定链接的测试示例):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM