为了账号安全,请及时绑定邮箱和手机立即绑定

.NET MVC 中带有 HttpClient 的单元测试控制器

.NET MVC 中带有 HttpClient 的单元测试控制器

C#
慕容3067478 2021-11-14 17:13:21
所以我有一个使用 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 issue在单元测试中努力正确模拟 httpclient 。如果您能深入了解如何编写控制器以在仍可测试的情况下进行服务调用,我们将不胜感激。
查看完整描述

2 回答

?
慕工程0101907

TA贡献1887条经验 获得超5个赞

所有使测试变慢的依赖项都应该被抽象出来。用抽象

包裹HttpClient起来,您可以在测试中模拟它。


public interface IMyClient

{

    Task<string> GetRawDataFrom(string url);

}

那么你的控制器将依赖于那个抽象


public class DemoController : Controller

{

    private readonly IMyClient _client;

    private string _baseUrl = "http://localhost:90/webservice";


    public DemoController(IMyClient client)

    {

        _client = client;

    }


    public async Task<ActionResult> Index()

    {

        var rawData = _client.GetRawDataFrom($"{_baseUrl}vehicle/menu/year");

        using (var reader = new StringReader(rawData))

        {

            var result = 

                (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(reader);

            return View("Index", result);

        }

    }

}

然后在测试中,您可以模拟您的抽象以返回预期数据


public class FakeClient : IMyClient

{

     public string RawData { get; set; }


     public Task<string> GetRawDataFrom(string url)

     {

         return Task.FromResult(RawData);

     }

}


[TestMethod]

public async Task Test_Index()

{

    // Arrange

    var fakeClient = new FakeClient 

    { 

        RawData = @"[ 

            { Name: "One", Path: "/one" },

            { Name: "Two", Path: "/two" }  

        ]" 

    };       

    DemoController controller = new DemoController(fakeClient);


    // Act

    var result = await controller.Index();

    ViewResult viewResult = (ViewResult)result;


    // Assert

    Assert.AreEqual("Index", viewResult.ViewName);

    Assert.IsNotNull(viewResult.Model);

}

实际实施将使用 HttpClient


public class MyHttpClient : IMyClient

     public Task<string> GetRawDataFrom(string url)

     {

         var response = await client.GetAsync(url);

         if (response.IsSuccessStatusCode)

         {

             return await response.Content.ReadAsStringAsync();

         }

     }

}


查看完整回答
反对 回复 2021-11-14
?
BIG阳

TA贡献1859条经验 获得超6个赞

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


这是您的控制器的外观。有几种方法可以做到这一点,但这种方法使用字符串扩展方法来完全抽象出客户端。(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上可用。


查看完整回答
反对 回复 2021-11-14
  • 2 回答
  • 0 关注
  • 194 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信