看到AcquireTokenForClientParameterBuilder 是通过外部库提供的,您显然无法对其进行修改以使其更具可测试性。鉴于此,我建议将代码抽象到您自己的接口后面(将适配器模式应用于测试目的)。
以以下服务/测试为例,说明您当前如何使用 IConfidentialClientApplication 并尝试模拟它(这会导致您看到相同的错误):
public class MyService
{
private readonly IConfidentialClientApplication _confidentialClientApplication;
public MyService(IConfidentialClientApplication confidentialClientApplication)
{
_confidentialClientApplication = confidentialClientApplication;
}
public async Task<string> GetAccessToken(IEnumerable<string> scopes)
{
AcquireTokenForClientParameterBuilder tokenBuilder = _confidentialClientApplication.AcquireTokenForClient(scopes);
AuthenticationResult token = await tokenBuilder.ExecuteAsync();
return token.AccessToken;
}
}
public class UnitTest1
{
[Fact]
public async Task Test1()
{
Mock<IConfidentialClientApplication> _appMock = new Mock<IConfidentialClientApplication>();
AuthenticationResult authentication = CreateAuthenticationResult("myToken");
_appMock
.Setup(_ => _.AcquireTokenForClient(It.IsAny<string[]>()).ExecuteAsync())
.ReturnsAsync(authentication);
var myService = new MyService(_appMock.Object);
string accessToken = await myService.GetAccessToken(new string[] { });
Assert.Equal("myToken", accessToken);
}
private AuthenticationResult CreateAuthenticationResult(string accessToken) =>
new AuthenticationResult(accessToken, true, null, DateTimeOffset.Now, DateTimeOffset.Now, string.Empty, null, null, null, Guid.Empty);
}
通过引入一个单独的接口,您的代码可以简单地依赖它,让您控制它的使用/测试方式:
public interface IIdentityClientAdapter
{
Task<string> GetAccessToken(IEnumerable<string> scopes);
}
public class IdentityClientAdapter : IIdentityClientAdapter
{
private readonly IConfidentialClientApplication _confidentialClientApplication;
public IdentityClientAdapter(IConfidentialClientApplication confidentialClientApplication)
{
_confidentialClientApplication = confidentialClientApplication;
}
public async Task<string> GetAccessToken(IEnumerable<string> scopes)
{
AcquireTokenForClientParameterBuilder tokenBuilder = _confidentialClientApplication.AcquireTokenForClient(scopes);
AuthenticationResult token = await tokenBuilder.ExecuteAsync();
return token.AccessToken;
}
}
public class MyService
{
private readonly IIdentityClientAdapter _identityClientAdapter;
public MyService(IIdentityClientAdapter identityClientAdapter)
{
_identityClientAdapter = identityClientAdapter;
}
public async Task<string> GetAccessToken(IEnumerable<string> scopes)
{
return await _identityClientAdapter.GetAccessToken(scopes);
}
}
public class UnitTest1
{
[Fact]
public async Task Test1()
{
Mock<IIdentityClientAdapter> _appMock = new Mock<IIdentityClientAdapter>();
_appMock
.Setup(_ => _.GetAccessToken(It.IsAny<string[]>()))
.ReturnsAsync("myToken");
var myService = new MyService(_appMock.Object);
string accessToken = await myService.GetAccessToken(new string[] { });
Assert.Equal("myToken", accessToken);
}
}
这个例子显然是琐碎的,但仍然应该适用。界面只需要适合您的需求即可。