模拟HttpMessageHandler时未调用SendAsync

问题描述 投票:0回答:1

我正在尝试模拟HttpMessageHandler,以便使用HttpClient测试一个类。

模拟似乎工作,结果很好,但在验证方法SendAsunc被调用的次数时,它返回0而不是1 ...

知道为什么吗?我是Moq的新手,并不知道它的所有错综复杂。

测试方法

[TestMethod]
public void LoginTest_ShouldTrue()
{
    // Setup the handler
    var mockHandler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
    mockHandler.Protected()
        // Setup the protected method to mock
        .Setup<Task<HttpResponseMessage>>(
            "SendAsync",
            ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()
        )
        // prepare the expected response of the mocked http call
        .ReturnsAsync(new HttpResponseMessage()
        {
            StatusCode = System.Net.HttpStatusCode.OK,
            Content = new StringContent("You are connected as xxxxxxxxxxxxxxxx.")
        })
        .Verifiable();

    // Use HttpClient with mocked HttpMessageHandler
    var httpClient = new HttpClient(mockHandler.Object);

    var objectToTest = new LoginApi(new Uri("https://test.com/"), ref httpClient);

    // Perform test
    var user = "test_user";
    var pass = "test_password_123";
    objectToTest.LoginAsync(user, pass).Result.Should().Be(true);
    objectToTest.IsLoggedIn.Should().Be(true);

    // Check the http call was as expected
    var expectedUri = new Uri("https://test.com/cgi/session.pl");
    var formVariables = new List<KeyValuePair<string, string>>(3);
    formVariables.Add(new KeyValuePair<string, string>(".submit", "Sign+in"));
    formVariables.Add(new KeyValuePair<string, string>("user_id", user));
    formVariables.Add(new KeyValuePair<string, string>("password", pass));
    var expectedContent = new FormUrlEncodedContent(formVariables);
    mockHandler.Protected().Verify<Task<HttpResponseMessage>>(
        "SendAsync",
        Times.Exactly(1), // We expected a single external request
        ItExpr.Is<HttpRequestMessage>(req =>
            req.Method == HttpMethod.Post // We expected a POST request
            && req.RequestUri == expectedUri // to this uri
            && req.Content == expectedContent // with this content
        ),
        ItExpr.IsAny<CancellationToken>()
    );
}

测试方法

public class LoginApi : ILoginApi
{
    private readonly Uri baseUri;
    private readonly HttpClient client;

    public bool IsLoggedIn { get; protected set; }

    public LoginApi(Uri baseUri, ref HttpClient client)
    {
        this.baseUri = baseUri;
        this.client = client;
        IsLoggedIn = false;
    }

    public async Task<bool> LoginAsync(string user, string pass)
    {
        var formVariables = new List<KeyValuePair<string, string>>(3);
        formVariables.Add(new KeyValuePair<string, string>(".submit", "Sign+in"));
        formVariables.Add(new KeyValuePair<string, string>("user_id", user));
        formVariables.Add(new KeyValuePair<string, string>("password", pass));

        var targetUri = new Uri(baseUri, string.Join('/', URLs.ServiceCgiSuffix, "session.pl"));
        var res = await client.PostAsync(targetUri, new FormUrlEncodedContent(formVariables));

        var content = await res.Content.ReadAsStringAsync();
        IsLoggedIn = content.Contains("You are connected as");
        return IsLoggedIn;
    }

    public async Task<bool> LogoutAsync()
    {
        var formVariables = new List<KeyValuePair<string, string>>(1);
        formVariables.Add(new KeyValuePair<string, string>(".submit", "Sign-out"));

        var targetUri = new Uri(baseUri, string.Join('/', URLs.ServiceCgiSuffix, "session.pl"));
        var res = await client.PostAsync(targetUri, new FormUrlEncodedContent(formVariables));

        var content = await res.Content.ReadAsStringAsync();
        IsLoggedIn = !content.Contains("See you soon!");
        return !IsLoggedIn;
    }
}

错误详情

StackTrace: 
at Moq.Mock.VerifyCalls(Mock targetMock, InvocationShape expectation, LambdaExpression expression, Times times, String failMessage) in C:\projects\moq4\src\Moq\Mock.cs:line 414
    at Moq.Mock.Verify[T,TResult](Mock`1 mock, Expression`1 expression, Times times, String failMessage) in C:\projects\moq4\src\Moq\Mock.cs:line 315
    at Moq.Protected.ProtectedMock`1.Verify[TResult](String methodName, Times times, Object[] args) in C:\projects\moq4\src\Moq\Protected\ProtectedMock.cs:line 171
    at OpenFoodFacts.Test.Login.LoginApiTest.LoginTest_ShouldTrue() in path\to\project\LoginApiTest.cs:line 56
Result message: 
Test method OpenFoodFacts.Test.Login.LoginApiTest.LoginTest_ShouldTrue threw exception: 
Moq.MockException: 
Expected invocation on the mock exactly 1 times, but was 0 times:
mock => mock.SendAsync(
    It.Is<HttpRequestMessage>(req => (req.Method == POST && req.RequestUri == https://test.com/cgi/session.pl) && req.Content == FormUrlEncodedContent),
    It.IsAny<CancellationToken>())

Configured setups: 
HttpMessageHandler mock => mock.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())

Performed invocations: 
HttpMessageHandler.SendAsync(Method: POST, RequestUri: 'https://test.com/cgi/session.pl', Version: 2.0, Content: System.Net.Http.FormUrlEncodedContent, Headers:
{
    Content-Type: application/x-www-form-urlencoded
}, CancellationToken)
c# .net .net-core moq dotnet-httpclient
1个回答
0
投票

问题是请求内容是否相等,比较两个对象与==运算符如果两个操作数引用同一个对象则返回true;他们没有。如果您删除此行“&& req.Content == expectedContent”,您的测试将正常工作。除此之外,您测试和模拟设置工作正常。

© www.soinside.com 2019 - 2024. All rights reserved.