如何在单元测试中使用FttItEasy和HttpClient?

Pur*_*ome 7 .net c# unit-testing fakeiteasy

我试图弄清楚如何使用HttpClient使用FakeItEasy,给出以下代码:

public Foo(string key, HttpClient httpClient = null)
{ .. }

public void DoGet()
{
    ....

    if (_httpClient == null)
    {
        _httpClient = new HttpClient();
    }

    var response = _httpClient.GetAsync("user/1);
}

public void DoPost(foo Foo)
{
    if (_httpClient == null)
    {
        _httpClient = new HttpClient();
    }

    var formData = new Dictionary<string, string>
    {
        {"Name", "Joe smith"},
        {"Age", "40"}
    };    

    var response = _httpClient.PostAsync("user", 
        new FormUrlEncodedContent(formData));
}
Run Code Online (Sandbox Code Playgroud)

所以我不知道如何使用FakeItEasy,伪造出HttpClient GetAsyncPostAsync方法.

生产代码不会在HttpClient中传递,但单元测试将传递由FakeItEasy制作的假实例.

例如.

[Fact]
public void GivenBlah_DoGet_DoesSomething()
{
    // Arrange.
    var httpClient A.Fake<HttpClient>(); // <-- need help here.
    var foo = new Foo("aa", httpClient);

    // Act.
    foo.DoGet();

    // Assert....
}
Run Code Online (Sandbox Code Playgroud)

更新:

我认为FiE(以及大多数模拟软件包)可以在接口或虚拟方法上运行.因此,对于这个问题,让刚刚prentendGetAsyncPostAsync方法是虚拟化......请:)

Cra*_* W. 12

这是我的(或多或少)通用FakeHttpMessageHandler.

public class FakeHttpMessageHandler : HttpMessageHandler
{
    private HttpResponseMessage _response;

    public static HttpMessageHandler GetHttpMessageHandler( string content, HttpStatusCode httpStatusCode )
    {
        var memStream = new MemoryStream();

        var sw = new StreamWriter( memStream );
        sw.Write( content );
        sw.Flush();
        memStream.Position = 0;

        var httpContent = new StreamContent( memStream );

        var response = new HttpResponseMessage()
        {
            StatusCode = httpStatusCode,
            Content = httpContent
        };

        var messageHandler = new FakeHttpMessageHandler( response );

        return messageHandler;
    }

    public FakeHttpMessageHandler( HttpResponseMessage response )
    {
        _response = response;
    }

    protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken )
    {
        var tcs = new TaskCompletionSource<HttpResponseMessage>();

        tcs.SetResult( _response );

        return tcs.Task;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是我的一个测试中使用它的一个例子,它希望将一些JSON作为返回值.

const string json = "{\"success\": true}";

var messageHandler = FakeHttpMessageHandler.GetHttpMessageHandler( 
    json, HttpStatusCode.BadRequest );
var httpClient = new HttpClient( messageHandler );
Run Code Online (Sandbox Code Playgroud)

你现在将httpClient注入你正在测试的类中(使用你喜欢的任何注入机制),当GetAsync你被调用时,你messageHandler会吐出你告诉它的结果.


Zen*_*uka 7

您还可以创建一个AbstractHandler可以拦截公共抽象方法的方法。例如:

public abstract class AbstractHandler : HttpClientHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return Task.FromResult(SendAsync(request.Method, request.RequestUri.AbsoluteUri));
    }

    public abstract HttpResponseMessage SendAsync(HttpMethod method, string url);
}
Run Code Online (Sandbox Code Playgroud)

然后你可以拦截类似的调用AbstractHandler.SendAsync(HttpMethod method, string url)

// Arrange
var httpMessageHandler = A.Fake<AbstractHandler>(options => options.CallsBaseMethods());
A.CallTo(() => httpMessageHandler.SendAsync(A<HttpMethod>._, A<string>._)).Returns(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Result")});
var httpClient = new HttpClient(httpMessageHandler);

// Act
var result = await httpClient.GetAsync("https://google.com/");

// Assert
Assert.Equal("Result", await result.Content.ReadAsStringAsync());
A.CallTo(() => httpMessageHandler.SendAsync(A<HttpMethod>._, "https://google.com/")).MustHaveHappenedOnceExactly();
Run Code Online (Sandbox Code Playgroud)

更多信息可以在此博客上找到:https://www.meziantou.net/mocking-an-httpclient-using-an-httpclienthandler.htm