VB.NET中如何Mock HttpResponseMessage的SendAsync方法?
解决VB.NET中Mock HttpClient的Protected Friend方法SendAsync的问题
问题核心
你遇到的两个错误根源在于:
SendAsync是HttpMessageHandler的Protected Friend方法,无法直接通过常规Setup访问- VB.NET中Moq处理Protected方法的语法和C#不同,不能直接用Lambda表达式,必须使用方法名字符串+参数类型的方式指定要Mock的方法
解决方案步骤
1. 导入必要的命名空间
确保测试类顶部导入Moq.Protected命名空间,这是Moq处理Protected方法的扩展所在:
Imports Moq Imports Moq.Protected Imports System.Net.Http Imports System.Threading Imports System.Threading.Tasks
2. 修正Mock Setup的语法
在VB.NET中,Mock Protected方法需要通过方法名的字符串来指定,并用ItExpr.IsAny替代It.IsAny(Protected方法Mock专用的匹配器)。同时因为SendAsync返回Task(Of HttpResponseMessage),需要用ReturnsAsync来返回异步结果。
另外,业务代码中调用了两个不同的API接口,需要根据请求的RequestUri返回对应的数据,避免两个请求返回相同内容导致测试不准确。
3. 修正测试方法的异步调用
因为GetUserPreferenceResponseData是异步方法,测试方法必须标记为Async Sub,并使用Await调用,否则拿到的result只是未完成的Task,无法正确断言。
修正后的完整测试代码
<TestMethod()> Public Async Sub GetUserPreferenceResponseData() 'Arrange Dim methodName = System.Reflection.MethodBase.GetCurrentMethod().Name Dim testUserId As Long = 123 ' 指定测试用的ID Dim testTenant = "TestTenant" ' 模拟Account.CurrentTenant的值 ' 模拟UserContents接口返回的数据 Dim asuc As New ActivitySummary.UserContent() With {.UserContentId = 1, .UserContentName = "BB"} Dim ucList As New List(Of ActivitySummary.UserContent) From {asuc} Dim userContentJson = Newtonsoft.Json.JsonConvert.SerializeObject(ucList) ' 模拟Users接口返回的数据 Dim testUserPrefs As New UserPreferences() With {.Id = testUserId} Dim userPrefsJson = Newtonsoft.Json.JsonConvert.SerializeObject(testUserPrefs) ' 模拟Account的CurrentTenant属性 Dim accountMock = New Mock(Of IAccount)() accountMock.Setup(Function(x) x.CurrentTenant).Returns(testTenant) ' Mock HttpMessageHandler Dim handlerMock = New Mock(Of HttpMessageHandler)() handlerMock.Protected() _ .Setup(Of Task(Of HttpResponseMessage))("SendAsync", _ ItExpr.IsAny(Of HttpRequestMessage), _ ItExpr.IsAny(Of CancellationToken)) _ .ReturnsAsync(Function(request As HttpRequestMessage, token As CancellationToken) ' 根据请求路径返回对应的数据 If request.RequestUri.ToString().EndsWith("/api/v1/UserContents") Then Return New HttpResponseMessage With { .StatusCode = Net.HttpStatusCode.OK, .Content = New StringContent(userContentJson) } ElseIf request.RequestUri.ToString().EndsWith($"/api/v1/Users/{testTenant}/{testUserId}") Then Return New HttpResponseMessage With { .StatusCode = Net.HttpStatusCode.OK, .Content = New StringContent(userPrefsJson) } Else ' 其他请求返回404 Return New HttpResponseMessage(Net.HttpStatusCode.NotFound) End If End Function) ' 创建HttpClient Dim httpClient = New HttpClient(handlerMock.Object) ' 初始化待测试的业务类(假设其他依赖已正确Mock) Dim userPreferencesBL As New UserPreferencesBL(_Context, accountMock.Object, _dbConfigManager) 'Action Dim result = Await userPreferencesBL.GetUserPreferenceResponseData(testUserId, httpClient) 'Assert Assert.IsNotNull(result, $"{methodName} returned null, expected User Preferences.") Assert.AreEqual(testUserId, result.Id) Assert.IsNotNull(result.UserContentsList) Assert.AreEqual(1, result.UserContentsList.Count) Assert.AreEqual("BB", result.UserContentsList(0).UserContentName) End Sub
关键语法说明
handlerMock.Protected():开启Protected方法的Mock模式.Setup(Of Task(Of HttpResponseMessage))("SendAsync", ...):指定要Mock的方法名(字符串)和返回类型ItExpr.IsAny(Of T):用于Protected方法的参数匹配,替代常规的It.IsAnyReturnsAsync(Function(...)):异步返回结果,支持根据请求参数动态返回不同内容
这样修改后,就能解决你遇到的两个错误,同时正确Mock两个不同的API请求,完成业务逻辑的单元测试。
内容的提问来源于stack exchange,提问作者CyberTooth Tiger
相关产品推荐
相关产品推荐

