在 C# 单元测试中使用 HttpContext.GetTokenAsync
Using HttpContext.GetTokenAsync in C# Unit Tests
我正在尝试为我的控制器 class 编写单元测试,它使用以下命令检索令牌:
string token = await HttpContext.GetTokenAsync("access_token");
因此我使用以下代码模拟了 HttpContext:
public static HttpContext MakeFakeContext()
{
var serviceProvider = new Mock<IServiceProvider>();
var authservice = new Mock<IAuthenticationService>();
authservice.Setup(_ => _.GetTokenAsync(It.IsAny<HttpContext>(), It.IsAny<string>())).Returns(Task.FromResult("token"));
serviceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authservice);
return new DefaultHttpContext
{
RequestServices = serviceProvider.Object
};
}
我正在设置模拟上下文:
var mockcontext = MakeFakeContext();
unitUnderTest.ControllerContext = new ControllerContext
{
HttpContext = mockcontext
};
现在,当我 运行 进行单元测试时,出现以下错误:
System.NotSupportedException : Unsupported expression: _ => _.GetTokenAsync(It.IsAny(), It.IsAny())
Extension methods (here: AuthenticationTokenExtensions.GetTokenAsync) may not be used in setup / verification expressions.
在我的研究过程中,我偶然发现了一些解决方案,您可以在其中模拟幕后涉及的特定部分,这些部分不属于扩展。这些是其中的一些:, 。第二个显示了类似的问题,我尝试后似乎有效。但由于某种原因,它不适用于 GetTokenAsync 方法。
你们有什么提示吗?
这是该扩展方法的源代码:
public static Task<string> GetTokenAsync(this HttpContext context, string scheme,
string tokenName) =>
context.RequestServices.GetRequiredService<IAuthenticationService>
().GetTokenAsync(context, scheme, tokenName);
依次调用这个扩展方法:
public static async Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName)
{
if (auth == null)
{
throw new ArgumentNullException(nameof(auth));
}
if (tokenName == null)
{
throw new ArgumentNullException(nameof(tokenName));
}
var result = await auth.AuthenticateAsync(context, scheme);
return result?.Properties?.GetTokenValue(tokenName);
}
最终结果是调用 AuthenticateAsync
,如 var result = await auth.AuthenticateAsync(context, scheme);
.
行所示
既然你不能修改扩展方法,也许你可以编写自己的模拟方法?
我不确定在对具有扩展方法的对象进行模拟时最佳做法是什么,所以也许有人可以扩展这个答案。
值得注意的是AuthenticateAsync
不是扩展方法,您可以找到代码here.
如@Nkosi 所述:
模拟 IServiceProvider
和 IAuthenticationService
.
恕我直言,看到真实的代码总是有用的,这样您就可以识别和理解它在幕后做了什么,并完全模拟出所有需要的部分,所以我将保留上面的内容。
这里以 Zer0 的回答为基础,是一个使用 Moq 的示例:
private void MockHttpContextGetToken(
Mock<IHttpContextAccessor> httpContextAccessorMock,
string tokenName, string tokenValue, string scheme = null)
{
var authenticationServiceMock = new Mock<IAuthenticationService>();
httpContextAccessorMock
.Setup(x => x.HttpContext.RequestServices.GetService(typeof(IAuthenticationService)))
.Returns(authenticationServiceMock.Object);
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), scheme));
authResult.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = tokenName, Value = tokenValue }
});
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(httpContextAccessorMock.Object.HttpContext, scheme))
.ReturnsAsync(authResult);
}
这对我有用
controller.ControllerContext = new ControllerContext();
var serviceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
authResult.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = "access_token", Value = "accessTokenValue" }
});
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
serviceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
controller.ControllerContext.HttpContext = new DefaultHttpContext
{
User = user,
RequestServices = serviceProvider.Object
};
我正在尝试为我的控制器 class 编写单元测试,它使用以下命令检索令牌:
string token = await HttpContext.GetTokenAsync("access_token");
因此我使用以下代码模拟了 HttpContext:
public static HttpContext MakeFakeContext()
{
var serviceProvider = new Mock<IServiceProvider>();
var authservice = new Mock<IAuthenticationService>();
authservice.Setup(_ => _.GetTokenAsync(It.IsAny<HttpContext>(), It.IsAny<string>())).Returns(Task.FromResult("token"));
serviceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authservice);
return new DefaultHttpContext
{
RequestServices = serviceProvider.Object
};
}
我正在设置模拟上下文:
var mockcontext = MakeFakeContext();
unitUnderTest.ControllerContext = new ControllerContext
{
HttpContext = mockcontext
};
现在,当我 运行 进行单元测试时,出现以下错误:
System.NotSupportedException : Unsupported expression: _ => _.GetTokenAsync(It.IsAny(), It.IsAny()) Extension methods (here: AuthenticationTokenExtensions.GetTokenAsync) may not be used in setup / verification expressions.
在我的研究过程中,我偶然发现了一些解决方案,您可以在其中模拟幕后涉及的特定部分,这些部分不属于扩展。这些是其中的一些:
你们有什么提示吗?
这是该扩展方法的源代码:
public static Task<string> GetTokenAsync(this HttpContext context, string scheme,
string tokenName) =>
context.RequestServices.GetRequiredService<IAuthenticationService>
().GetTokenAsync(context, scheme, tokenName);
依次调用这个扩展方法:
public static async Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName)
{
if (auth == null)
{
throw new ArgumentNullException(nameof(auth));
}
if (tokenName == null)
{
throw new ArgumentNullException(nameof(tokenName));
}
var result = await auth.AuthenticateAsync(context, scheme);
return result?.Properties?.GetTokenValue(tokenName);
}
最终结果是调用 AuthenticateAsync
,如 var result = await auth.AuthenticateAsync(context, scheme);
.
既然你不能修改扩展方法,也许你可以编写自己的模拟方法?
我不确定在对具有扩展方法的对象进行模拟时最佳做法是什么,所以也许有人可以扩展这个答案。
值得注意的是AuthenticateAsync
不是扩展方法,您可以找到代码here.
如@Nkosi 所述:
模拟 IServiceProvider
和 IAuthenticationService
.
恕我直言,看到真实的代码总是有用的,这样您就可以识别和理解它在幕后做了什么,并完全模拟出所有需要的部分,所以我将保留上面的内容。
这里以 Zer0 的回答为基础,是一个使用 Moq 的示例:
private void MockHttpContextGetToken(
Mock<IHttpContextAccessor> httpContextAccessorMock,
string tokenName, string tokenValue, string scheme = null)
{
var authenticationServiceMock = new Mock<IAuthenticationService>();
httpContextAccessorMock
.Setup(x => x.HttpContext.RequestServices.GetService(typeof(IAuthenticationService)))
.Returns(authenticationServiceMock.Object);
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), scheme));
authResult.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = tokenName, Value = tokenValue }
});
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(httpContextAccessorMock.Object.HttpContext, scheme))
.ReturnsAsync(authResult);
}
这对我有用
controller.ControllerContext = new ControllerContext();
var serviceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
authResult.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = "access_token", Value = "accessTokenValue" }
});
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
serviceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
controller.ControllerContext.HttpContext = new DefaultHttpContext
{
User = user,
RequestServices = serviceProvider.Object
};