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

本文关键字:属性 响应 控制器 操作 单元测试 NET ASP 何使用 MVC | 更新日期: 2023-09-27 18:34:12

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

[HttpGet]
public IActionResult Get()
{
    ...
    Response.Headers.Add("Location", location);
    ...
}

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

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

但是在 ASP.NET 5中,Response没有二传手,只有一个getter。
如何在单元测试中设置Response值?

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

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

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

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

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

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
};

这比我希望看到的模拟单个属性的代码要多一些,但是我还找不到更好的方法,而且它似乎运行良好。

您可以使用具有虚拟属性的 ResponseWrapper,您可以模拟它并期望使用"Location"参数调用虚拟 AddHeader 方法。相关问题这里: MOQ - 模拟 MVC 控制器的响应.Cookies.Clear((

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

  • http://www.jerriepelser.com/blog/unit-testing-controllers-aspnet5
  • http://dotnetthoughts.net/creating-unit-tests-for-asp-net-mvc-6-applications/

如果你想尝试NancyFx(http://nancyfx.org/((https://github.com/NancyFx/Nancy(,另一种选择很容易:https://github.com/NancyFx/Nancy/wiki/Testing-your-application

例如 - 如果您正在测试登录页面并想要验证 如果用户输入,则用户被重定向到"错误"页面 您可能使用应该重定向到的错误凭据 断言:

[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);
}