18

HttpContext我知道和之间的区别HttpContextWrapper如下...

HttpContext

这是老式的 asp.net 上下文。这样做的问题是它没有基类并且不是虚拟的,因此无法用于测试(不能模拟它)。建议不要将其作为函数参数传递,而是传递 HttpContextBase 类型的变量。

HttpContextBase

这是 HttpContext 的(c# 3.5 的新功能)替换。由于它是抽象的,它现在是可模拟的。这个想法是,您希望传递上下文的函数应该期望接收其中之一。它具体由 HttpContextWrapper 实现

HttpContextWrapper

C# 3.5 中的新功能 - 这是 HttpContextBase 的具体实现。要在普通网页中创建其中之一,请使用 new HttpContextWrapper(HttpContext.Current)。

这个想法是,为了使您的代码可单元测试,您将所有变量和函数参数声明为 HttpContextBase 类型,并使用 IOC 框架(例如 Castle Windsor)将其注入。在普通代码中,castle 是注入“new HttpContextWrapper (HttpContext.Current)”的等价物,而在测试代码中,您将获得一个 HttpContextBase 的模拟。

但我不知道它的真正用途。我听说与 Web 表单相比,它在单元测试中很有用。但它有什么用?


我也知道我们可以使用它来执行这里提到的控制器和动作

4

1 回答 1

19

我听说与 Web 表单相比,它在单元测试中很有用。但它有什么用?

让我们举一个 ASP.NET MVC 控制器操作的示例,该操作将 cookie 添加到响应中:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var cookie = new HttpCookie("foo", "bar");
        this.Response.Cookies.Add(cookie);
        return View();
    }
}

注意那里的 Response 属性。这是一个HttpResponseBase. 所以我们可以在单元测试中模拟它:

public class HttpResponseMock: HttpResponseBase
{
    private HttpCookieCollection cookies;
    public override HttpCookieCollection Cookies
    {
        get
        {
            if (this.cookies == null)
            {
                this.cookies = new HttpCookieCollection();
            }

            return this.cookies;
        }
    }
}

public class HttpContextMock: HttpContextBase
{
    private HttpResponseBase response;

    public override HttpResponseBase Response
    {
        get 
        {
            if (this.response == null)
            {
                this.response = new HttpResponseMock();
            }
            return this.response;
        }
    }
}

现在我们可以编写一个单元测试:

// arrange
var sut = new HomeController();
var httpContext = new HttpContextMock();
sut.ControllerContext = new ControllerContext(httpContext, new RouteData(), sut);

// act
var actual = sut.Index();

// assert
Assert.AreEqual("bar", sut.Response.Cookies["foo"].Value);

而且由于所有成员都是虚拟的,我们可以使用一个模拟框架,这将避免我们为单元测试编写这些模拟类的需要。例如,使用NSubstitute时,测试的外观如下:

// arrange
var sut = new HomeController();
var context = Substitute.For<HttpContextBase>();
var response = Substitute.For<HttpResponseBase>();
var cookies = new HttpCookieCollection();
context.Response.Returns(response);
context.Response.Cookies.Returns(cookies);
sut.ControllerContext = new ControllerContext(context, new RouteData(), sut);

// act
var actual = sut.Index();

// assert
Assert.AreEqual("bar", sut.Response.Cookies["foo"].Value);

现在让我们使用一个 WebForm:

protected void Page_Load(object sender, EventArgs)
{
    var cookie = new HttpCookie("foo", "bar");
    this.Response.Cookies.Add(cookie);
}

在这种情况下, Response 属性是具体的HttpResponse。所以你被淘汰了。不可能单独进行单元测试。

于 2013-05-27T06:09:59.190 回答