【问题标题】:HttpContext.Current is null when unit test单元测试时 HttpContext.Current 为空
【发布时间】:2016-07-03 13:34:42
【问题描述】:

我有以下 Web Api 控制器方法。

当我通过网络运行此代码时,HttpContext.Current 是 never null 并给出所需的值。

public override void Post([FromBody]TestDTO model)
{

    var request = HttpContext.Current.Request;
    var testName = request.Headers.GetValues("OS Type")[0];
    // more code

}

然而,当我从Unit Test、HttpContext.Current is always null.调用这个方法时

我该如何解决?

【问题讨论】:

  • 从测试项目中调用控制器方法是有意义的。没有 http 上下文,因为您没有运行 Web 应用程序。解决方案是模拟请求。
  • 如何模拟请求?你能分享一个简单的例子吗?
  • 不是最好的例子...我不想使用任何模拟框架。
  • 然后通过继承HttpRequest创建一个mock类并覆盖你使用的方法和成员。然而,模拟框架是一个更好的主意。

标签: c# unit-testing asp.net-web-api


【解决方案1】:

在单元测试期间,HttpContext 始终为null,因为它通常由 IIS 填充。你有几个选择。

当然,你可以嘲笑HttpContext,(你真的不应该这样做 - Don't mock HttpContext!!!!他不喜欢被嘲笑!),。你真的应该尽量避免在你的代码中与HttpContext 紧密耦合。尝试将其限制在一个中心区域 (SRP);

相反,弄清楚您想要实现的功能是什么,并围绕它设计一个抽象。这将使您的代码更具可测试性,因为它与HttpContext 的耦合不是那么紧密。

根据您的示例,您正在寻找访问标头值。这只是在使用HttpContext 时如何改变您的想法的一个示例。

你原来的例子有这个

var request = HttpContext.Current.Request;
var testName = request.Headers.GetValues("OS Type")[0];

当你在寻找这样的东西时

var testName = myService.GetOsType();

然后创建一个提供该服务的服务

public interface IHeaderService {
    string GetOsType();
}

可以有一个具体的实现,比如

public class MyHeaderService : IHeaderService {

    public string GetOsType() {
        var request = HttpContext.Current.Request;
        var testName = request.Headers.GetValues("OS Type")[0];
        return testName;
    }
}

现在在您的控制器中,您可以进行抽象,而不是与HttpContext 紧密耦合

public class MyApiController : ApiController {
    IHeaderService myservice;
    public MyApiController(IHeaderService headers) {
        myservice = headers;
    }

    public IHttpActionResult Post([FromBody]TestDTO model) {    
        var testName = myService.GetOsType();
        // more code

    }    
}

你可以稍后注入你的具体类型来获得你想要的功能。

为了测试,你可以交换依赖项来运行你的测试。

如果被测试的方法是你的 Post() 方法,你可以创建一个假依赖或使用一个模拟框架

[TestClass]
public class MyTestClass {

    public class MyFakeHeaderService : IHeaderService {
        string os;
        public MyFakeHeaderService(string os) {
            this.os = os;
        }

        public string GetOsType() {
            return os;
        }
    }

    [TestMethod]
    public void TestPostMethod() {
        //Arrange
        IHeaderService headers = new MyFakeHeaderService("FAKE OS TYPE");
        var sut = new MyApiController(headers);
        var model = new TestDTO();

        //Act
        sut.Post(model);

        //Assert
        //.....
    }
}

【讨论】:

  • 优秀的答案和解释。我理解你的观点,可以解决我的问题。谢谢
  • 2014 年的一个很好的参考:docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… 这有一些其他重要的细节,例如控制反转,这将允许您在控制器实例化期间调用您的自定义上下文类。
【解决方案2】:

这是设计使然,它始终为空。但是 Nuget 上有一个 FakeHttpContext 项目,您可以简单地使用它。

要安装 FakeHttpContext,请在包管理器控制台 (PMC) 中运行以下命令

Install-Package FakeHttpContext 

然后像这样使用它:

using (new FakeHttpContext())
{
HttpContext.Current.Session["mySession"] = "This is a test";       
}

访问https://www.nuget.org/packages/FakeHttpContext安装包

参见 Github 上的示例:https://github.com/vadimzozulya/FakeHttpContext#examples

希望这会有所帮助:)

【讨论】:

    【解决方案3】:

    你只需要

    controller.Request = new HttpRequestMessage();
    controller.Configuration = new HttpConfiguration();
    

    来自unit-testing-controllers-in-web-api

    【讨论】:

      猜你喜欢
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 1970-01-01
      • 2019-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多