web-dev-qa-db-ja.com

ASP.NET Core MVC APIコントローラーでのAuthorizeAttributeのユニットテスト

単体テストが必要なコントローラーを備えたASP.NET Core MVC APIがあります。

コントローラー:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace TransitApi.Api.Controllers
{
    [Route("api/foo")]
    public class FooController : Controller
    {
        private IFooRepository FooRepository { get; }

        public FooController(IFooRepository fooRepository)
        {
            FooRepository = fooRepository;
        }

        [HttpGet]
        [Authorize("scopes:getfoos")]
        public async Task<IActionResult> GetAsync()
        {
            var foos = await FooRepository.GetAsync();
            return Json(foos);
        }
    }
}

AuthorizeAttributeの有効性を単体テストできることが不可欠です。コードベースで、属性が欠けていてスコープが正しくないという問題がありました。 この答え はまさに私が探しているものですが、Microsoft.AspNetCore.Mvc.ControllerActionInvokerメソッドがないことは、この方法では実行できないことを意味します。

単体テスト:

[Fact]
public void GetAsync_InvalidScope_ReturnsUnauthorizedResult()
{
    // Arrange
    var fooRepository = new StubFooRepository();
    var controller = new FooController(fooRepository)
    {
        ControllerContext = new ControllerContext
        {
            HttpContext = new FakeHttpContext()
            // User unfortunately not available in HttpContext
            //,User = new User() { Scopes = "none" }
        }
    };

    // Act
    var result = controller.GetAsync().Result;

    // Assert
    Assert.IsType<UnauthorizedResult>(result);
}

正しいスコープを持たないユーザーがコントローラーメソッドへのアクセスを拒否されていることを単体テストするにはどうすればよいですか?

現在、私は以下のようにAuthorizeAttributeの存在をテストすることで解決していますが、これは本当に十分ではありません:

    [Fact]
    public void GetAsync_Analysis_HasAuthorizeAttribute()
    {
        // Arrange
        var fooRepository = new StubFooRepository();
        var controller = new FooController(fooRepository)
        {
            ControllerContext = new ControllerContext
            {
                HttpContext = new FakeHttpContext()
            }
        };

        // Act
        var type = controller.GetType();
        var methodInfo = type.GetMethod("GetAsync", new Type[] { });
        var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);

        // Assert
        Assert.True(attributes.Any());
    }
12
Ivan

属性はリクエストパイプラインを処理するときにフレームワークによって評価されるため、インメモリテストサーバーとの統合テストが必要になります。

ASP.NET Coreでの統合テスト

統合テストでは、アプリケーションのコンポーネントが一緒に組み立てられたときに正しく機能することを確認します。 ASP.NET Coreは、ユニットテストフレームワークを使用した統合テストと、ネットワークオーバーヘッドなしで要求を処理するために使用できる組み込みテストWebホストをサポートしています。

[Fact]
public async Task GetAsync_InvalidScope_ReturnsUnauthorizedResult() {
    // Arrange
    var server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
    var client = server.CreateClient();
    var url = "api/foo";
    var expected = HttpStatusCode.Unauthorized;

    // Act
    var response = await client.GetAsync(url);

    // Assert
    Assert.AreEqual(expected, response.StatusCode);
}

テストが実際の実稼働実装に影響を与えたくない場合は、DIの依存関係をスタブ/モックに置き換えるテスト専用のスタートアップを作成することもできます。

7
Nkosi

あなたができることは、匿名フィルターミドルウェアを追加するようにテストサーバーを設定することです:

private HttpClient CreatControllerClient()
{
        return _factory.WithWebHostBuilder(builder
            => builder.ConfigureTestServices(services =>
            {
                // allow anonymous access to bypass authorization
                services.AddMvc(opt => opt.Filters.Add(new AllowAnonymousFilter()));
            })).CreateClient();
}
2
codeMoh