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

时间:2014-10-17 16:29:56

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

我正在尝试对我的控制器进行单元测试,但是一旦此控制器使用其嵌入的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无法模拟{ {1}}类,因为它应该模拟的方法(UrlHelper)不可覆盖,或类似的东西(我没有详述)。因为我使用的是WebApi 1.但是基于我的代码(以及许多其他帖子)的博客文章也使用了V1。所以我不明白为什么它不起作用,最重要的是,我怎么能让它发挥作用。

谢谢!

2 个答案:

答案 0 :(得分:2)

不确定您关联的文档是否自原始帖子后更新了,但是他们展示了一个模拟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);
}

答案 1 :(得分:1)

因此,您需要模拟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);
}