1

我正在尝试测试一个使用 HttpClient 来访问外部 API 的公共方法(方法 A)。这个公共方法调用同一个类的私有方法(方法B)来获取方法A的HttpClient发送请求所需的Access Token。我遇到的问题是我正在创建 HttpClientFactory 接口的模拟以测试方法 A 的响应,但是为了让方法 B 获得令牌,它需要自己的 HttpClient 实例。因此,在 Test 方法中创建的 mock 实例也将被 Method B 使用,并且尝试获取 Access Token 将失败。下面的代码使场景更加清晰。

待测方法(方法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吗?

4

1 回答 1

1

天下没有免费的午餐——如果想用外部依赖对一些代码进行单元测试,那么每个外部依赖都必须被模拟

或者我们可以在测试金字塔上再上一层进行集成测试(尽管这可能不是我们的情况)。

所以,你可以:

  1. 以与模拟( )_httpClientFactory相同的方式模拟 Token 响应SendAsync..._httpClientFactoryMock.Setup(x => x.CreateClient()).Returns(GetMockedHttpClient(expectedResponse));...

  2. ITokenProvider或者以不直接从 API 检索令牌的方式重新组织代码 - 创建一些更容易模拟的单方法接口。

    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);...
    
于 2019-08-03T15:08:14.403 回答