模拟HttpContext用于单元测试.NET核心MVC控制器?

Jam*_*zba 68 c# asp.net-mvc unit-testing asp.net-core

我在控制器中有一个函数,我是单元测试,期望http请求的标头中的值.我无法初始化HttpContext,因为它只是readonly.

我的控制器函数需要"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);
}
Run Code Online (Sandbox Code Playgroud)

没有使用第三方库,有没有直接的方法来做到这一点?

Jam*_*zba 176

我能够以这种方式初始化httpcontext和标头:

[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
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 我一直在努力寻找这个答案一个小时,对诸如“模拟请求标头”之类的东西进行搜索都无济于事。我终于通过谷歌搜索“模拟标题词典”找到了这一点。 (3认同)

Gle*_*lls 17

而不是模拟出HTTPContext,将标题映射到方法的参数可能是更好的主意.例如,在此答案底部的控制器中,id参数设置为值标题,名称等于"device-id"...单元测试然后变为

[TestMethod]
public void TestValuesController()
{
    ValuesController controller = new ValuesController();
    var result = controller.GetHeaderValue("27");
    Assert.AreEqual(result, "27");
}
Run Code Online (Sandbox Code Playgroud)

虽然您可以模拟HttpContext,但在我看来,除非您别无选择,否则应该避免使用它.FromHeaderAttribute的文档可以在这里找到FromHeaderAttribute类.

public class ValuesController: Controller
{
    public string GetHeaderValue([FromHeader(Name = "device-id")] string id)
    {
        return id;
    }
}
Run Code Online (Sandbox Code Playgroud)


ada*_*ncy 7

对于需要标头以及HttpContext 中的附加数据的人,您可以通过使用 DefaultHttpContext 类的第二个构造函数的功能初始化上下文来实现此目的:

1. 使用您需要的标头创建一个标头字典:

var headers = new Dictionary<string, StringValues>
{
   { "myHeaderKey", "myHeaderValue" },
};
var headerDictionary = new HeaderDictionary(headers)
Run Code Online (Sandbox Code Playgroud)

2. 使用之前创建的标头字典创建 HttpRequestFeature:

var requestFeature = new HttpRequestFeature()
{
    Headers = headerDictionary,
};
Run Code Online (Sandbox Code Playgroud)

3. 创建包含先前创建的特征的特征集合:

 var features = new FeatureCollection();

features.Set<IHttpRequestFeature>(requestFeature);
Run Code Online (Sandbox Code Playgroud)

4. 使用功能集合初始化 DefaultHttpContext,并将其设置为控制器的 HttpContext:

var httpContext = new DefaultHttpContext(features);

var controller = new MyController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = httpContext;
Run Code Online (Sandbox Code Playgroud)

控制器的上下文将设置正确的标头,并且您仍然可以根据需要向上下文提供更多数据,方法是在实例化 DefaultHttpContext 之前向 featureCollection 设置其他 HttpContext 属性(例如feature.Set<IQueryFeature>(new QueryFeature(...))查询字符串)。

PS:有关使用功能来模拟(以及一般的单元测试)HttpContext 的更深入说明,请参阅:https: //weblogs.asp.net/ricardoperes/unit-testing-the-httpcontext-in-controllers