用于单元测试 .NET 核心 MVC 控制器的模拟 HttpContext?
Posted
技术标签:
【中文标题】用于单元测试 .NET 核心 MVC 控制器的模拟 HttpContext?【英文标题】:Mock HttpContext for unit testing a .NET core MVC controller? 【发布时间】:2017-05-14 23:29:21 【问题描述】:我在控制器中有一个函数,我正在对它进行单元测试,该函数需要 http 请求标头中的值。我无法初始化 HttpContext,因为它是只读的。
我的控制器函数需要“device-id”的 http 请求标头值
[TestMethod]
public void TestValuesController()
ValuesController controller = new ValuesController();
//not valid controller.HttpContext is readonly
//controller.HttpContext = new DefaultHttpContext();
var result = controller.Get();
Assert.AreEqual(result.Count(), 2);
有没有不使用第三方库的直接方法来做到这一点?
【问题讨论】:
不要使用HttpContext?使用控制器的全部意义在于数据来自控制器的参数。如果您的控制器使用 HttpContext 来读取数据,就好像它是一个 WebForms 页面一样,那么您就有问题了。 @PanagiotisKanavos 标头中的值是一条信息,指示呼叫来自哪个移动设备。这是检索正确数据所必需的。设备 ID 位于标头中,因为身份验证需要该 ID,由自定义操作过滤器处理。我可以将设备 ID 作为路由参数传递,但这将是多余的 检查 FromHeaderAttribute 但也检查重复项。 HttpContext 现在可以通过配置注入了 我建议您编辑您的问题以准确指定您想要的内容(访问标题字段以识别移动设备)。 ASP.NET 文档似乎正在经历一个....“过渡”时期,说得好听点,缺少文档页面。检查询问如何路由移动设备的this almost identical question 【参考方案1】:我能够以这种方式初始化 httpcontext 和 header:
[TestMethod]
public void TestValuesController()
ValuesController controller = new ValuesController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.ControllerContext.HttpContext.Request.Headers["device-id"] = "20317";
var result = controller.Get();
//the controller correctly receives the http header key value pair device-id:20317
...
【讨论】:
【参考方案2】:与其模拟 HTTPContext,不如将标头映射到方法的参数中可能是一个更好的主意。例如,在这个答案底部的控制器中,id
参数被设置为名称等于“device-id”的值头......然后单元测试变为
[TestMethod]
public void TestValuesController()
ValuesController controller = new ValuesController();
var result = controller.GetHeaderValue("27");
Assert.AreEqual(result, "27");
虽然您可以模拟 HttpContext,但在我看来,除非您别无选择,否则应该避免这种情况。 FromHeaderAttribute 的文档可以在这里找到FromHeaderAttribute Class。
public class ValuesController: Controller
public string GetHeaderValue([FromHeader(Name = "device-id")] string id)
return id;
【讨论】:
在我的情况下,IIRC,要求将其包含在 http 标头中,因为需要在 .net 核心中间件组件中评估相同的值【参考方案3】:对于在 HttpContext 中需要标头但还需要其他数据的人,您可以通过使用 features 初始化上下文来做到这一点,这要归功于 DefaultHttpContext 的第二个构造函数类:
1。使用您需要的标题创建一个标题字典:
var headers = new Dictionary<string, StringValues>
"myHeaderKey", "myHeaderValue" ,
;
var headerDictionary = new HeaderDictionary(headers)
2。使用之前创建的头字典创建一个 HttpRequestFeature:
var requestFeature = new HttpRequestFeature()
Headers = headerDictionary,
;
3。创建一个包含先前创建的特征的特征集合:
var features = new FeatureCollection();
features.Set<IHttpRequestFeature>(requestFeature);
4。使用特征集合初始化 DefaultHttpContext,并将其设置为控制器的 HttpContext:
var httpContext = new DefaultHttpContext(features);
var controller = new MyController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = httpContext;
控制器的上下文将设置正确的标头,您仍然可以通过在实例化 DefaultHttpContext 之前为 featureCollection 设置额外的 HttpContext 属性来根据需要为上下文提供更多数据(例如,查询字符串的feature.Set<IQueryFeature>(new QueryFeature(...))
)。
PS:有关使用功能模拟(以及一般的单元测试)HttpContext 的更深入解释,请参阅:https://weblogs.asp.net/ricardoperes/unit-testing-the-httpcontext-in-controllers
【讨论】:
以上是关于用于单元测试 .NET 核心 MVC 控制器的模拟 HttpContext?的主要内容,如果未能解决你的问题,请参考以下文章
在 ASP.NET Core MVC API 控制器上对 AuthorizeAttribute 进行单元测试
RedirectToAction 不适用于 Tempdata - ASP.net 核心 MVC