我正在尝试在.NET Core环境中使用NUnit和Moq测试以下方法:
[HttpGet]
public async Task<IActionResult> DeviceType()
{
string deviceIp = HttpContext.GetServerVariable("REMOTE_ADDR");
var result = await DeviceService.GetTypeForAsync(deviceIp);
return Ok(result);
}
问题是模拟的
HttpContext.GetServerVariable
。我尝试模拟上下文并通过 ControllerContext
: 插入它
var actionContext = new Mock<ActionContext>();
var httpContext = new Mock<HttpContext>();
httpContext.Setup(x => x.GetServerVariable(It.IsAny<string>())).Returns("192.168.1.1");
actionContext.Setup(x => x.HttpContext).Returns(httpContext.Object);
//...
var deviceController = new DeviceController();
deviceController.ControllerContext = new ControllerContext(actionContext.Object);
但是测试未通过并抛出错误:
System.NotSupportedException:不支持的表达式:x => x.GetServerVariable(It.IsAny()) 扩展方法(此处:HttpContextServerVariableExtensions.GetServerVariable)不得在设置/验证表达式中使用。
问题是:如何模拟
HttpContext.GetServerVariable("REMOTE_ADDR")
?
如错误所示,您无法模拟扩展方法,因为这是静态方法,而不是可重写或可实现的实例或接口方法。
方法位于
HttpContextServerVariableExtensions
中,仅从上下文中获取IServerVariablesFeature
功能并将调用转发给该实现。
因此,还要模拟
IServerVariablesFeature
及其 this[string]
索引器,并让该模拟的对象从您模拟的 HttpContext.Features
返回。
感谢 @martin-costello 和 @codecaster 的精彩提示,我终于能够模拟它了:
var serverVarsFeature = new Mock<IServerVariablesFeature>();
serverVarsFeature.Setup(x => x["REMOTE_ADDR"]).Returns("192.168.1.1");
var httpContext = new Mock<HttpContext>();
httpContext.Setup(x => x.Features.Set(serverVarsFeature.Object));
// ...
var deviceController = new DeviceController();
deviceController.ControllerContext = new ControllerContext();
deviceController.ControllerContext.HttpContext = new DefaultHttpContext(httpContext.Object.Features);
我得到“对象引用未设置到对象的实例。”使用@1-bug的解决方案时出现异常。 当将它与 @codecaster 的答案和 @martin-costello 的评论结合起来时,我设法得到了一个解决方案,在 HTTP 上下文上调用
GetServerVariable
时返回正确的值。
var mockServerVars = new Mock<IServerVariablesFeature>();
mockServerVars
.Setup(m => m["REMOTE_ADDR"])
.Returns("192.168.1.1");
var mockFeatures = new Mock<IFeatureCollection>();
mockFeatures
.Setup(m => m.Get<IServerVariablesFeature>())
.Returns(mockServerVars.Object);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext
.SetupGet(m => m.Features)
.Returns(mockFeatures.Object);
与@1_bug的解决方案相比,差异是
IFeatureCollection
的中间步骤,它返回服务器变量功能。x.Features.Set
,而是用我的模拟功能集合替换整个 x.Features
。当在 HTTP 上下文中调用
GetServerVariable
时,它将尝试从 IServerVariablesFeature
属性(即从我模拟的功能集合中)获取 HttpContext.Features
。发现这样的功能存在(不为空),GetServerVariable
将使用模拟的服务器变量功能来获取IP地址。