簡體   English   中英

.NET MVC中帶有HttpClient的單元測試控制器

[英]Unit test Controller with HttpClient in .NET MVC

因此,我有一個使用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。 如果能對如何編寫控制器以進行服務調用同時仍可進行測試提供任何見解,將對您的幫助大有幫助。

應該簡化所有使測試變慢的依賴項。
用抽象包裝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();
         }
     }
}

在不使用服務包裝程序, 模擬程序或IoC容器的情況下測試HttpClient調用的另一種方法是使用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上可用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM