提问人:CyberTooth Tiger 提问时间:10/13/2023 最后编辑:CyberTooth Tiger 更新时间:11/6/2023 访问量:66
如何在 VB.NET 中模拟 HttpResponseMessage SendAsync?
How can I Mock HttpResponseMessage SendAsync in VB.NET?
问:
我找到了一百篇关于如何在 C# 中执行此操作的帖子,但没有一篇关于如何在 VB.Net 中执行此操作的帖子。每一次将这些方法转化为 VB.NET 的尝试都失败了。这似乎归结为 SendAsync 是“受保护的朋友”这一事实,如以下错误图像所示: <在马克·西曼(Mark Seeman)的指导下被移除 - 见下文>
如果您看不到图像,则我收到的错误是:“HttpMessageHandler.Protected Friend MustOverloads Function SendAsync(request As HttpRequestMessage, candellationToken As CancellationToken) As Task(Of HttpResponseMessage)”在此上下文中无法访问,因为它是“受保护的朋友”。错误号为 BC30390,这会导致非常有用的Microsoft错误页面“抱歉,我们没有关于此 Visual Basic 错误的详细信息”。
首先,这是一个跨越多个部门的应用程序的庞然大物,更改为 C# 不是一种选择。我正在对业务逻辑的一部分进行单元测试,该逻辑从另一个系统调用 API,然后在返回该数据后执行其他代码并返回结果。测试数据不能指望从外部 API 返回的结果,因此我需要模拟 API 结果以与我的测试数据匹配。这是我尝试测试的 BL 函数的那部分:
Public Sub New(context As IUSASContext, account As IAccount, configurationManager As IDBConfigurationManager)
_Context = context
_Account = account
_AppSettingsConfigManager = configurationManager
_UserPreferenceHistory = New UserPreferenceHistory(_Context, _Account)
End Sub
Public Async Function GetUserPreferenceResponseData(id As Long, httpClient As HttpClient) As Task(Of UserPreferences)
Dim baseUri = New Uri(AppSettingsHelper.GetValue(AppSettingName.ActivitySummaryApiUri, "", _AppSettingsConfigManager))
Dim userContentReponse = Await httpClient.GetAsync(New Uri(baseUri, "/api/v1/UserContents"))
Dim userContentData = Await userContentReponse.Content.ReadAsStringAsync()
Dim userResponse = Await httpClient.GetAsync(New Uri(baseUri, $"/api/v1/Users/{_Account.CurrentTenant}/{id}"))
Dim userData = Await userResponse.Content.ReadAsStringAsync()
Dim userPreferences As UserPreferences = Newtonsoft.Json.JsonConvert.DeserializeObject(Of UserPreferences)(userData)
userPreferences.SchedulesList = GetUserPreferencesEmailScheduleList()
userPreferences.UserContentsList = Newtonsoft.Json.JsonConvert.DeserializeObject(Of List(Of Opm.Staffing.Models.ActivitySummary.UserContent))(userContentData)
userPreferences.Id = id
Return userPreferences
End Function
首先,我尝试模拟 HttpClient 本身,并设置“GetAsync”函数,但事实证明这是不可能的。快速的 Web 搜索证明,普遍的共识是模拟 HttpResponseMessage 并设置“SendAsync”。但是,C# 示例显示将“Protected”放在“Setup”关键字之前,如以下示例所示:
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{'name':thecodebuzz,'city':'USA'}"),
});
当我尝试这样做时,我收到以下错误: <在马克·西曼(Mark Seeman)的指导下被移除 - 见下文> 如果您看不到错误,则文本为:“Lambda 表达式无法转换为'String',因为'String'不是委托类型。
这是我的单元测试的最新版本:
<TestMethod()> Public Sub GetUserPreferenceResponseData()
'Arrange
Dim methodName = System.Reflection.MethodBase.GetCurrentMethod().Name
Dim asuc As New ActivitySummary.UserContent() With {.UserContentId = 1, .UserContentName = "BB"}
Dim uc As New List(Of ActivitySummary.UserContent) From {asuc}
' the below produces the "Protected Friend" intellesense error
'_HTTPMessageHandlerMock.Setup(Function(x) x.SendAsync(It.IsAny(Of HttpRequestMessage), It.IsAny(Of Threading.CancellationToken))) _
.Returns(New HttpResponseMessage With {.Content = New StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(uc))})
' the below produces the "String not a Delegate Type " intellesense error
_HTTPMessageHandlerMock.Protected().Setup(Function(x) x.SendAsync(It.IsAny(Of HttpRequestMessage), It.IsAny(Of Threading.CancellationToken))) _
.Returns(New HttpResponseMessage With {.Content = New StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(uc))})
Dim _Client = New HttpClient(_HTTPMessageHandlerMock.Object)
Dim userPreferencesBL As New UserPreferencesBL(_Context, _Account, _dbConfigManager)
Dim uid As Integer
'Action
Dim result = userPreferencesBL.GetUserPreferenceResponseData(uid, _Client)
'Assert
Assert.IsNotNull(result, methodName + " returned null, expected User Preferences.")
End Sub
将鼠标悬停在“SendAsync”上会显示我上面提到的错误。
我一直在兜圈子,因为改变一件事会破坏另一件事,我以为我已经解决了。任何帮助将不胜感激。
答:
这样的事情应该可以工作:
Dim httpMessageHandlerMock As New Mock(Of HttpMessageHandler)
httpMessageHandlerMock _
.Protected() _ ' Needs Imports Moq.Protected
.Setup(Of Task(Of HttpResponseMessage))(
"SendAsync",
ItExpr.IsAny(Of HttpRequestMessage),
ItExpr.IsAny(Of Threading.CancellationToken)) _
.ReturnsAsync(
New HttpResponseMessage() With
{
.Content = New JsonConvert.SerializeObject(uc))
})
Dim client = New HttpClient(httpMessageHandlerMock.Object)
请注意,正如评论所说,您需要
Imports Moq.Protected
否则,扩展方法和该 API 的其余部分将不可用。Protected
您不能将 lambda 表达式或其他强类型表达式与访问权限一起使用,因为该方法仅对继承类可见,而 test 或 不继承自 。因此,您需要用字符串 () 标识要覆盖的方法,并使用 API 匹配输入参数。Protected
httpMessageHandlerMock
HttpMessageHandler
"SendAsync"
ItExpr
作为使用 HttpClient VB.NET 进行类似测试的人,我已经实现了自己的测试,它覆盖了 以“处理”请求并生成受控内容。DelegatingHandler
SendAsync
我将发布我正在做的事情的简化版本,但关键是,根据要测试的方法路径,使用一组处理程序构建。TestDelegatingHandler
Private ReadOnly cgHandlers As Dictionary(Of String, Func(Of HttpRequestMessage, HttpResponseMessage))
''' <summary>
''' Creates a new <see cref="TestDelegatingHandler"/> with custom handlers.
''' </summary>
''' <param name="handlers">
''' The handlers as a Tuple of LocalPath As <see cref="String"/> and Handler
''' As <see cref="Func(Of HttpRequestMesage, HttpResponseMessage)"/>.
''' </param>
Public Sub New(
ParamArray handlers() As (LocalPath As String, Handler As Func(Of HttpRequestMessage, HttpResponseMessage))
)
cgHandlers = handlers.ToDictionary(Function(x) x.LocalPath, Function(x) x.Handler)
End Sub
''' <inheritdoc />
Protected Overrides Async Function SendAsync(
request As HttpRequestMessage,
cancellationToken As Threading.CancellationToken
) As Task(Of HttpResponseMessage)
Dim rc As HttpResponseMessage
Dim message As String
Dim handler As Func(Of HttpRequestMessage, HttpResponseMessage) = Nothing
Dim localPath As String
localPath = request.RequestUri.LocalPath
If cgHandlers.TryGetValue(localPath, handler) Then
rc = handler.Invoke(request, message)
Else
rc = New HttpResponseMessage(Net.HttpStatusCode.NotFound) With {
.RequestMessage = request
}
End If
Return Await Task.FromResult(rc)
End Function
然后,Used 接受处理程序以创建客户端。IHttpClientFactory
Private Shared Function GetHttpClientFactory(delegatingHandler As DelegatingHandler) As IHttpClientFactory
Dim mock As Mock(Of IHttpClientFactory)
mock = New Mock(Of IHttpClientFactory)
mock.Setup(Function(x) x.CreateClient(It.IsAny(Of String)())).Returns(Function() New HttpClient(delegatingHandler))
Return mock.Object
End Function
或者(基于您的代码),只需向客户端提供:
New HttpClient(New TestDelegatingHandler(...handlers...))
它不会通过样式处理验证,但您绝对可以控制响应消息。Mock(Of )
评论