尝试为 ASP.Net Core 3.1 单元测试创建 Mock.Of<ControllerContext>() 时出错
Error trying to create Mock.Of<ControllerContext>() for ASP.Net Core 3.1 Unit Test
根据 Moq 快速入门的最后一部分定义 here,我正在尝试配置以下 Mock 以便将表单值传递给被测控制器方法:
var formCollection = new FormCollection(
new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
var controllerContext = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext.Request.Form == formCollection);
controller.ControllerContext = controllerContext;
但是,当 运行 测试时,它在 Mock.Of<>
行失败并出现以下错误:
System.NotSupportedException : Unsupported expression: mock => mock.HttpContext
Non-overridable members (here: ActionContext.get_HttpContext) may not be used in setup / verification expressions.
我错过了什么?我不是按照快速入门文档中定义的示例做的吗?
错误是因为ControllerContext.HttpContext
属性不是virtual
,所以Moq无法覆盖它。
考虑使用实际的 ControllerContext
并模拟 HttpContext
以分配给 属性
var formCollection = new FormCollection(new Dictionary<string, StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
var controllerContext = new ControllerContext() {
HttpContext = Mock.Of<HttpContext>(ctx => ctx.Request.Form == formCollection)
};
controller.ControllerContext = controllerContext;
//...
甚至使用 DefaultHttpContext
并分配所需的值
var formCollection = new FormCollection(new Dictionary<string, StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
HttpContext httpContext = new DefaultHttpContext();
httpContext.Request.Form = formCollection;
var controllerContext = new ControllerContext() {
HttpContext = httpContext
};
controller.ControllerContext = controllerContext;
//...
您需要手动设置所有未标记 virtual
的属性,方法是在 lambda 中显式设置它们,或者可能在外部设置它们(无需创建具体对象),这是通过我至少在版本 4.16.1.
方法一(使用Mock.Of()
时):
var mockCtx = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext == Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
== formCollection))`
注意:如果您需要设置要模拟的所有属性,或做任何需要 Mock.Get()
的事情,如 this answer 您需要直接在 HttpContext
上做在 Mock.Get(mockCtx.HttpContext )
而不是 mockCtx
.
方法二(使用new Mock<>()
时):
var mockCtx = new Mock<ControllerContext>();
mockCtx.Object.HttpContext = Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
== formCollection);
无需创建具体对象,因为方法 2 只是使用模拟对象做同样的事情。
注意:这仅在 属性 不是只读的情况下才有效。
根据 Moq 快速入门的最后一部分定义 here,我正在尝试配置以下 Mock 以便将表单值传递给被测控制器方法:
var formCollection = new FormCollection(
new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
var controllerContext = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext.Request.Form == formCollection);
controller.ControllerContext = controllerContext;
但是,当 运行 测试时,它在 Mock.Of<>
行失败并出现以下错误:
System.NotSupportedException : Unsupported expression: mock => mock.HttpContext
Non-overridable members (here: ActionContext.get_HttpContext) may not be used in setup / verification expressions.
我错过了什么?我不是按照快速入门文档中定义的示例做的吗?
错误是因为ControllerContext.HttpContext
属性不是virtual
,所以Moq无法覆盖它。
考虑使用实际的 ControllerContext
并模拟 HttpContext
以分配给 属性
var formCollection = new FormCollection(new Dictionary<string, StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
var controllerContext = new ControllerContext() {
HttpContext = Mock.Of<HttpContext>(ctx => ctx.Request.Form == formCollection)
};
controller.ControllerContext = controllerContext;
//...
甚至使用 DefaultHttpContext
并分配所需的值
var formCollection = new FormCollection(new Dictionary<string, StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
HttpContext httpContext = new DefaultHttpContext();
httpContext.Request.Form = formCollection;
var controllerContext = new ControllerContext() {
HttpContext = httpContext
};
controller.ControllerContext = controllerContext;
//...
您需要手动设置所有未标记 virtual
的属性,方法是在 lambda 中显式设置它们,或者可能在外部设置它们(无需创建具体对象),这是通过我至少在版本 4.16.1.
方法一(使用Mock.Of()
时):
var mockCtx = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext == Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
== formCollection))`
注意:如果您需要设置要模拟的所有属性,或做任何需要 Mock.Get()
的事情,如 this answer 您需要直接在 HttpContext
上做在 Mock.Get(mockCtx.HttpContext )
而不是 mockCtx
.
方法二(使用new Mock<>()
时):
var mockCtx = new Mock<ControllerContext>();
mockCtx.Object.HttpContext = Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
== formCollection);
无需创建具体对象,因为方法 2 只是使用模拟对象做同样的事情。
注意:这仅在 属性 不是只读的情况下才有效。