我正在尝试测试使用HttpClient访问外部API的公共方法(方法A)。此公共方法调用同一类的私有方法(方法B)来获取方法A的HttpClient发送请求所必需的访问令牌。我遇到的问题是,为了测试方法A的响应,我正在创建HttpClientFactory接口的模拟,但是为了使方法B获得令牌,它需要自己的HttpClient实例。因此,在Test方法中创建的模拟实例也将由方法B使用,并且它将无法尝试获取访问令牌。以下代码使情况更加清晰。
要测试的方法(方法A):
public async Task<HttpResponseMessage> SendAsync(string requestUri, string siteName, int accountId)
{
try
{
var accessToken = await GetTokenAsync(siteName, accountId);
if (accessToken == null)
throw new ArgumentNullException("Error Sending request - Could not find an access token");
var request = new HttpRequestMessage(HttpMethod.Get, $"{accessToken.Api}{requestUri}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Accesstoken);
var httpClient = _httpClientFactory.CreateClient();
return await httpClient.SendAsync(request);
}
catch (Exception e)
{
throw new Exception("Error Sending request.", e);
}
}
测试方法:
[Fact]
public async Task ShouldReturnHttpResponseMessage_OnSendAsync()
{
//_jaClientMock.Setup(x => x.GetTokenAsync(It.IsAny<string>(), It.IsAny<int>())).Verifiable();
_appSettingsMock.Setup(x => x.Value)
.Returns(GetValidFakeAppSettings());
HttpResponseMessage expectedResponse = GetListOfContacts(HttpStatusCode.OK, false);
_httpClientFactoryMock.Setup(x => x.CreateClient())
.Returns(GetMockedHttpClient(expectedResponse));
var response = await _jaClient.SendAsync("someurl", "siteName", 1000);
response.IsSuccessStatusCode.ShouldBeTrue();
}
私有方法(方法B):
private async Task<AccessToken> GetTokenAsync(string siteName, int accountId)
{
try
{
if (_cache.TryGetValue(GetCacheKeyForToken(siteName, accountId), out AccessToken value))
return value;
....
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
accessToken = await response.Content.ReadAsAsync<AccessToken>();
}
.....
return accessToken;
}
catch (Exception e)
{
throw new Exception("Error Getting an Access Token.", e);
}
}
任何想法我如何测试方法A?
答案 0 :(得分:1)
There ain't no such thing as a free lunch-如果要对具有外部依赖项的某些代码进行单元测试,则必须每个外部依赖项都进行模拟。
或者可以一步一步test pyramid进行集成测试(虽然可能不是我们的情况)。
因此,您可以:
在_httpClientFactory
中模拟令牌响应的方式与在SendAsync
(..._httpClientFactoryMock.Setup(x => x.CreateClient()).Returns(GetMockedHttpClient(expectedResponse));...
)中模拟令牌响应的方式相同
或以不直接从API检索令牌的方式重组代码-创建一些单一方法ITokenProvider
界面,将使其更易于模拟。
public interface ITokenProvider
{
public async Task<AccessToken> GetTokenAsync(string siteName, int accountId);
}
...
public async Task<HttpResponseMessage> SendAsync(string requestUri, string siteName, int accountId)
{
try
{
var accessToken = await _tokenProvider.GetTokenAsync(siteName, accountId);
...
[Fact]
public async Task ShouldReturnHttpResponseMessage_OnSendAsync()
{
var tokenProviderMock = new Mock<ITokenProvider>()
.Setup(o => o.GetTokenAsync("siteName", 1000))
.Returns(Constants.AllowedToken);
_jaClient = new JaClient(tokenProviderMock.Object);...