如何测试具有私有方法依赖项的公共函数?

时间:2019-08-03 14:56:16

标签: c# .net-core xunit httpclientfactory

我正在尝试测试使用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?

1 个答案:

答案 0 :(得分:1)

There ain't no such thing as a free lunch-如果要对具有外部依赖项的某些代码进行单元测试,则必须每个外部依赖项都进行模拟

或者可以一步一步test pyramid进行集成测试(虽然可能不是我们的情况)。

因此,您可以:

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

  2. 或以不直接从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);...