[英]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.