簡體   English   中英

在 Thread 的上下文中使用 Moq 模擬 HttpClient

[英]Using Moq to mock HttpClient in the context of Thread

我正在使用moq創建單元測試,但我無法弄清楚如何將此框架應用於我處理使用HttpClient的程序的一部分。 我發現有各種資源可以演示如何直接模擬HttpClient響應,但是我的應用程序使用HttpClient的方式與使用Thread略有不同。

測試的骨架:

public class MyTestClass
{
    public void myTest()
    {
        ClassA classObj = new ClassA();
        classObj.Start();
        // I'd like to use moq somewhere here to mock the response that occurs in DoThreadStuff() below
    }
}

正在測試的 class:

public class ClassA
{
    private readonly Thread _myThread; 
    private HttpClient _client;
    
    public ClassA()
    {
        // initialize some values
        _myThread = new Thread(DoThreadStuff);
    }
        
    public void Start()
    {
        _myThread.Start(); // starts DoThreadStuff()
    }
    
    private void DoThreadStuff()
    {
        var newClient = getNewHttpClient(); // utility function returns a HttpClient
        var response = newClient.GetAsync("/my/api/status/endpoint");
    }       
}

如您所見,當ClassA.Start()時,會通過GetAsync創建和使用一個新的HttpClient 為這個構建測試的正確方法是什么? 我是否必須更改現有課程的實施以適應最小起訂量? 有沒有人有非常相似的經驗,我可以看看?

假設您的ClassA如下所示:

public class ClassA
{
    private readonly Thread _myThread;

    public ClassA()
    {
        _myThread = new Thread(DoThreadStuff);
    }

    public void Start()
    {
        _myThread.Start();
    }

    private void DoThreadStuff()
    {
        var newClient = getNewHttpClient();

        var response = newClient.GetAsync("https://httpstat.us//200").GetAwaiter().GetResult();
        if(response.StatusCode == HttpStatusCode.OK)
            Console.WriteLine("OK");
        else if(response.StatusCode == HttpStatusCode.InternalServerError)
            Console.WriteLine("Not good");
    }

    protected virtual HttpClient getNewHttpClient()
    {
        return new HttpClient();
    }
}
  • 為了可測試性,我在getNewHttpClient調用之后添加了一些虛擬代碼
    • 請注意,以同步方式( .GetAwaiter().GetResult() )調用異步方法並不是一個好主意
  • 我還將getNewHttpClient添加到您的 class 作為protected virtual ,以便能夠輕松覆蓋它

現在讓我們創建一個輔助方法來模擬 HttpClient:

public static HttpClient SetupMockClient(HttpResponseMessage response)
{
    var mockMessageHandler = new Mock<HttpMessageHandler>();
    mockMessageHandler.Protected()
        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
        .ReturnsAsync(response);
    return new HttpClient(mockMessageHandler.Object);
}

讓我們從ClassA派生來覆蓋getNewHttpClient方法

internal class VerifiableClassA : ClassA
{
    private HttpClient mockedHttpClient;
    public VerifiableClassA(HttpClient mockedHttpClient)
    {
        this.mockedHttpClient = mockedHttpClient;
    }

    protected override HttpClient getNewHttpClient()
    {
        return this.mockedHttpClient;
    }
}

請注意,您也可以使用最小起訂量來執行操作,因此您無需引入新的 class 來進行測試。 但在這種情況下, getNewHttpClient應該是public


現在您可以像這樣執行單元測試:

var mockedClient = SetupMockClient(new HttpResponseMessage
{
    StatusCode = HttpStatusCode.InternalServerError,
    Content = new StringContent("Failure")
});

var sut = new VerifiableClassA(mockedClient);
sut.Start();

您應該考慮使用工廠/存儲庫模式。 它們使測試您的方法變得更容易,因為您可以只注入像 HttpClient 這樣的對象的模擬。 Peter Csala 建議的答案非常骯臟,因為您創建了一個 class,它的唯一目的是在測試中使用。

暫無
暫無

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

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