[英]Moq SetupSequence with multiple setup assertions
我正在编写一些测试,其中我正在测试的 class 取决于HttpClient
。 嘲笑我是 mocking 一个HttpMessageHandler
并将其传递给HttpClient
构造函数。
为此,我有一个基数 class:
public class HttpTestBase
{
protected static readonly string BaseAddress = "https://test.com";
protected readonly HttpClient _httpClient;
protected readonly Mock<HttpMessageHandler> _httpMessageHandlerMock;
public HttpTestBase()
{
_httpMessageHandlerMock = new Mock<HttpMessageHandler>();
_httpClient = new HttpClient(_httpMessageHandlerMock.Object);
_httpClient.BaseAddress = new Uri(BaseAddress);
}
protected void MockHttpResponse(HttpResponseMessage message, string expectedPath, HttpMethod expectedMethod)
{
_httpMessageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(request => AssertRequestParameters(expectedPath, expectedMethod, request)),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(message);
}
private bool AssertRequestParameters(string expectedPath, HttpMethod expectedMethod, HttpRequestMessage request)
{
// Throw an exception if the method or path does not match what is expected.
}
}
然后测试如下所示,在测试 class 中继承了这个测试库:
[Fact]
public async Task GetAvailableLicenseCount()
{
// Arrange
var licenses = new JsonObject
{
["total_seats_consumed"] = 4500,
["total_seats_purchased"] = 5000
};
MockHttpResponse(
new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(licenses.ToJsonString()) },
expectedPath: "/enterprises/enterprise/consumed-licenses",
expectedMethod: HttpMethod.Get
);
// Act
var result = await sut.GetAvailableLicenseCount();
// Assert
result.Should().Be(500);
}
当一个方法只执行一次调用时,这很有效。 一些方法按顺序执行多个调用。 我已经部分解决了这个问题,但我仍然希望在设置中有断言(例如,当我调用AssertRequestParameters
时)。
为了支持链中的多个调用,我改为在 base class 中执行此操作:
protected void AddHttpMockResponse(HttpResponseMessage message, string expectedPath, HttpMethod expectedMethod)
{
_responseMocks.Enqueue(new HttpMock
{
Response = message,
Assertion = new Assertion { ExpectedPath = expectedPath, ExpectedMethod = expectedMethod }
});
}
protected void MockHttpResponses()
{
_httpMessageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => _responseMocks.Dequeue().Response);
}
如您所见,我已经删除了设置中的断言,因为断言似乎发生了冲突,并且我从模拟中得到了错误的返回值。
为了使用它,我重构了测试如下:
[Fact]
public async Task GetAvailableLicenseCount()
{
// Arrange
var licenses = new JsonObject
{
["total_seats_consumed"] = 4500,
["total_seats_purchased"] = 5000
};
AddHttpMockResponse(
new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(licenses.ToJsonString()) },
expectedPath: "/enterprises/enterprise/consumed-licenses",
expectedMethod: HttpMethod.Get
);
MockHttpResponses();
// Act
var result = await sut.GetAvailableLicenseCount();
// Assert
result.Should().Be(500);
}
这也适用于来自同一模拟的多个响应。
但是,我希望能够模拟多个响应,其中响应由我在模拟Setup
中使用的内容决定。 我想这样做的原因是我无法在HttpMessageHandler
上使用Verify
,因为我无法访问该方法。
是否可以根据模拟设置中完成的操作获得动态返回值?
如果我正确理解你的问题,你会想要这样的东西:
我最近不得不做类似的事情,发现下面的文章非常有帮助。 检查“使用 Moq 模拟 HttpMessageHandler”部分。 它向您展示了如何返回您创建的HttpResponseMessage
object,这正是我认为您可能想要的。
https://code-maze.com/csharp-mock-httpclient-with-unit-tests/
class 上的字段
private readonly Mock<HttpMessageHandler> _httpMessageHandlerStub;
在您的设置中(构造函数或单独的方法)
_httpMessageHandlerStub = new Mock<HttpMessageHandler>();
var httpClient = new HttpClient(_httpMessageHandlerStub.Object);
_sut = new SomeService(
httpClient,
anotherDependency
)
然后您可以在每个测试中配置以下内容
[Fact]
public async Task GetListAsync_Successfully_Uses_Api_Key()
{
var unauthorisedResponseMessage = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{
Content = new StringContent($"'{_apiKeyHeaderName}' header not found or API key is incorrect")
};
var successResponseMessage = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(new List<SomeDto>()))
};
// Return 200 if api key header is present and the value is correct.
_httpMessageHandlerStub.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(x => x.Method == HttpMethod.Get && x.Headers.Any(h => h.Key == _apiKeyHeaderName && h.Value.FirstOrDefault() == _apiKeyHeaderValue)),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(successResponseMessage);
// Return 401 is api key header is not present or api key value is incorrect.
_httpMessageHandlerStub.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(x => x.Method == HttpMethod.Get && !x.Headers.Any(h => h.Key == _apiKeyHeaderName && h.Value.FirstOrDefault() == _apiKeyHeaderValue)),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(unauthorisedResponseMessage);
var result = _sut.GetListAsync();
// Assert stuff...
}
希望这会有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.