I have found literally a hundred posts on how to do this in C#, but not a one on how to do it in VB.Net. Every attempt to translate these methods into VB.NET has failed. It seems to boil down to the fact that the SendAsync is a "Protected Friend", as shown in the following error image: <removed at the direction of Mark Seeman - see below>
If you can't see the image, the error I'm getting is: 'HttpMessageHandler.Protected Friend MustOverride Overloads Function SendAsync(request As HttpRequestMessage, candellationToken As CancellationToken) As Task(Of HttpResponseMessage)' is not accessible in this context because it is 'Protected Friend'. The error number is BC30390, which leads to the exceedingly helpful Microsoft error page "Sorry, we don't have specifics on this Visual Basic error".
Firstly, this is a behemoth of an application crossing numerous divisions, and changing to C# is not an option. I am unit testing a section of business logic that calls an API from another system and then, upon return of that data, executes additional code and returns the result. The test data cannot count on results returned from the external API, so I need to mock the API result to match with my test data. Here is that portion of the BL Function that I am attempting to test:
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
First, I tried to Mock the HttpClient itself, and setting up the "GetAsync" function, which proved impossible. A quick web search proved that the general consensus was to, instead, mock the HttpResponseMessage and setup the "SendAsync". However, the C# examples show to put "Protected" prior to the "Setup" keyword, as in the following example:
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'}"),
});
When I try to do that, I get the following error: <removed at the direction of Mark Seeman - see below> If you can't see the error, the text is: "Lambda expression cannot be converted to 'String' because 'String' is not a delegate type."
Here is the latest version of my unit test:
<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
Mousing over "SendAsync" shows the error I mentioned above.
I keep going around in circles, as changing one thing will break another that I had thought I had fixed. Any help would be greatly appreciated.
Something like this ought to work:
Notice that, as the comment says, you need
because otherwise the
Protectedextension method and the rest of that API isn't available.You can't use lambda expressions or other strongly typed expressions with
Protectedaccess, because the method is only visible to inheriting classes, and the test, orhttpMessageHandlerMock, isn't inheriting fromHttpMessageHandler. Therefore, you instead need to identify the method you want to override with a string ("SendAsync") and use theItExprAPI to match on input arguments.