首页 > 解决方案 > .NET MVC 中带有 HttpClient 的单元测试控制器

问题描述

所以我有一个控制器,它使用 HttpClient 来调用 web 服务,如下所示:

public class DemoController : Controller
{
    HttpClient client;
    string baseUrl = "http://localhost:90/webservice";

    public DemoController()
    {
        client = new HttpClient
        {
            BaseAddress = new Uri(baseUrl)
        };

    }

    // GET: DemoInfo
    public async Task<ActionResult> Index()
    {
        HttpResponseMessage response = await client.GetAsync(baseUrl + "vehicle/menu/year");
        string content = "";
        MenuItems result = null;
        if (response.IsSuccessStatusCode)
        {
            content = await response.Content.ReadAsStringAsync();
            result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
        }
        return View("Index", result);
    }
}

我对此操作的单元测试如下:

    [TestMethod]
    public async Task Test_Index()
    {
        // Arrange
        DemoController controller = new DemoController();

        // Act
        var result = await controller.Index();
        ViewResult viewResult = (ViewResult) result;

        // Assert
        Assert.AreEqual("Index", viewResult.ViewName);
        Assert.IsNotNull(viewResult.Model);
    }

所以显然我想避免每次运行测试时都调用 Web 服务。在选择像 Unity 这样的 IoC 容器以便将 HttpClient 注入控制器时,我会走上正确的道路吗?这对我想要达到的目标来说是不是有点矫枉过正?我知道有很多人在通过这个github 问题在单元测试中正确地模拟 httpclient 的历史。任何帮助都将非常感谢您深入了解如何编写控制器以在仍可测试的同时进行服务调用。

标签: c#.netasp.net-mvcunit-testingdotnet-httpclient

解决方案


在没有服务包装器、模拟或 IoC 容器的情况下测试HttpClient调用的另一种方法是使用FlurlHttpClient ,这是一个围绕它提供(除其他外)一些强大的测试功能的小型包装器库。[免责声明:我是作者]

这是您的控制器的外观。有几种方法可以做到这一点,但这种方法使用完全抽象出客户端的字符串扩展方法。HttpClient(为您管理每个主机的单个实例以防止麻烦。)

using Flurl.Http;

public class DemoController : Controller
{
    string baseUrl = "http://localhost:90/webservice";

    // GET: DemoInfo
    public async Task<ActionResult> Index()
    {
        var content = await baseUrl
            .AppendPathSegment("vehicle/menu/year")
            .GetStringAsync();

        var result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
        return View("Index", result);
    }
}

和测试:

using Flurl.Http;

[TestMethod]
public async Task Test_Index()
{
    // fake & record all HTTP calls in the test subject
    using (var httpTest = new HttpTest())
    {
        // Arrange
        httpTest.RespondWith(200, "<xml>some fake response xml...</xml>");
        DemoController controller = new DemoController();

        // Act
        var result = await controller.Index();
        ViewResult viewResult = (ViewResult) result;

        // Assert
        Assert.AreEqual("Index", viewResult.ViewName);
        Assert.IsNotNull(viewResult.Model);
    }
}

Flurl.Http 在NuGet上可用。


推荐阅读