如何使用 ASP.NET 5 (MVC 6) 中的响应 属性 对控制器操作进行单元测试?

How to unit test a Controller action using the Response property in ASP.NET 5 (MVC 6)?

ASP.NET Core 1.0 (MVC 6) 项目中,我有一个控制器操作方法,我在其中使用 Response 属性设置 header:

[HttpGet]
public IActionResult Get()
{
    ...

    Response.Headers.Add("Location", location);

    ...
}

我试图为此操作方法实施单元测试,但是 Response 属性 的值为 null。

这在以前的ASP.NET版本中很容易解决,因为Response属性有一个setter,你可以简单地将它的值设置为a单元测试期间的新 HttpResponse 个实例。

但是在ASP.NET5中Response没有一个setter,只有一个getter.
如何在单元测试中设置 Response 的值?

更新:澄清一下:这个问题是关于ASP.NET Core 1.0的。另一个链接为重复的问题是关于 ASP.NET 4,Api 从那时起发生了变化,因此那里的答案不适用于这个问题。

您可以使用具有虚拟属性的 ResponseWrapper,您可以模拟它并期望一次调用带有 "Location" 参数的虚拟 AddHeader 方法。 相关问题这里:MOQ - Mocking MVC Controller's Response.Cookies.Clear()

您可以查看这些示例:for ASP.Net 5 MVC 6:

如果您想尝试 NancyFx 的另一种选择(http://nancyfx.org/) (https://github.com/NancyFx/Nancy) it's quite easy: https://github.com/NancyFx/Nancy/wiki/Testing-your-application

For example - if you were testing a login page and wanted to verify that the user was redirected to an “error” page if they entered incorrect credentials you could use the ShouldHaveRedirectedTo assertion:

[Fact] public void Should_redirect_to_login_with_error_details_incorrect() {
    // Given
    var bootstrapper = new DefaultNancyBootstrapper();
    var browser = new Browser(bootstrapper);

    // When
    var response = browser.Post("/login/", (with) => {
        with.HttpRequest();
        with.FormValue("Username", "username");
        with.FormValue("Password", "wrongpassword");
    });

    // Then
    response.ShouldHaveRedirectedTo("/login?error=true&username=username");

}

还有:

[Fact]
public void Should_display_error_message_when_error_passed()
{
    // Given
    var bootstrapper = new DefaultNancyBootstrapper();
    var browser = new Browser(bootstrapper);

    // When
    var response = browser.Get("/login", (with) => {
        with.HttpRequest();
        with.Query("error", "true");
    });

    // Then
    response.Body["#errorBox"]
            .ShouldExistOnce()
            .And.ShouldBeOfClass("floatingError")
            .And.ShouldContain(
                "invalid",
                StringComparison.InvariantCultureIgnoreCase);
}

我找到了一个解决问题的方法,但是,它有点乏味和复杂,所以我仍然有兴趣看到一种更简单的方法,如果存在的话。

ControllerActionContext.HttpContext 获取其 Response 属性 的值。让模拟变得如此困难的是所有这些属性都是只读的,所以我们不能简单地设置模拟值,我们必须为每个正在运行的对象创建模拟。

我在测试中需要的响应部分是 Headers 集合,因此我必须创建并使用以下模拟来使其可用。 (模拟是用 Moq 完成的。)

var sut = new MyController();

// The HeaderDictionary is needed for adding HTTP headers to the response.
// This needs a couple of different Mocks, because many properties in the class model are read-only.
var headerDictionary = new HeaderDictionary();
var response = new Mock<HttpResponse>();
response.SetupGet(r => r.Headers).Returns(headerDictionary);

var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(a => a.Response).Returns(response.Object);

sut.ActionContext = new ActionContext()
{
    HttpContext = httpContext.Object
};

这比我想看到的模拟单个 属性 的代码要多一些,但我还找不到更好的方法,而且它似乎工作得很好。