我正在尝试模拟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)
答案 0 :(得分:0)
问题在于请求内容是否相等,如果两个操作数引用同一个对象,则将两个对象与==运算符进行比较将返回true;他们没有。如果您删除此行
“ && req.Content == expectedContent
”
您的测试将正常进行。除此之外,您的测试和模拟设置都可以正常运行。
答案 1 :(得分:0)
晚了聚会,但希望这个答案可以帮助其他人。
关于您的断言失败的原因,另一个答案是正确的,但不要仅仅删除该行。验证请求是否与所需的有效负载一起发送非常重要。只需将其更改为:
&& req.Content.ToString() == expectedContent.ToString()
之所以可行,是因为字符串是C#中的原始类型,可以与==
进行比较。