web-dev-qa-db-ja.com

ASP.NET 5(MVC 6)のResponseプロパティを使用してコントローラーアクションを単体テストするにはどうすればよいですか?

ASP.NET Core 1.0(MVC 6)プロジェクトでは、Responseプロパティを使用してヘッダーを設定するControllerアクションメソッドがあります。

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

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

    ...
}

このアクションメソッドの単体テストを実装しようとしましたが、Responseプロパティの値がnullです。

Responseプロパティにはセッターがあり、単体テスト中にその値を新しいHttpResponseインスタンスに設定できるため、これは以前のバージョンのASP.NETでは簡単に解決できました。

ただし、ASP.NET 5では、Responseにはセッターがなく、ゲッターのみがあります。
単体テストでResponseの値を設定するにはどうすればよいですか?

[〜#〜] update [〜#〜]:明確にするために:この質問はASP.NET Core 1.についてです。重複としてリンクされているもう1つの質問は、ASP.NET 4に関するものであり、その後Apiが変更されたため、そこでの回答はこの質問には適用されません。

11
Mark Vincze

この問題の解決策を1つ見つけましたが、それは少し面倒で複雑なので、もっと簡単なアプローチがあれば、それでも興味があります。

Controllerは、そのResponseプロパティの値をActionContext.HttpContextから取得します。これをモックするのが難しいのは、これらのプロパティがすべて読み取り専用であるため、単にモック値を設定するだけではなく、プレイ中のすべてのオブジェクトに対してモックを作成する必要があることです。

テストで必要な応答の一部は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
};

これは、単一のプロパティをモックするために見たいコードよりも少し多いコードですが、これ以上のアプローチはまだ見つかりませんでした。うまく機能しているようです。

16
Mark Vincze

ResponseWrapperを仮想プロパティで使用でき、それをモックして、「Location」パラメーターを使用した仮想AddHeaderメソッドの呼び出しを1回期待できます。 関連する質問ここで: MOQ-MVCコントローラーのResponse.Cookies.Clear()をモックする

これらの例を確認できます:ASP.Net 5 MVC 6の場合:

NancyFx( http://nancyfx.org/ )( https://github.com/NancyFx/Nancy )を試してみたい場合の別のオプションは非常に簡単です:- https://github.com/NancyFx/Nancy/wiki/Testing-your-application

たとえば、ログインページをテストしていて、ユーザーが誤った資格情報を入力した場合にユーザーが「エラー」ページにリダイレクトされたことを確認したい場合は、ShouldHaveRedirectedToアサーションを使用できます。

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