単体テストが必要なコントローラーを備えた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.Controller
にActionInvoker
メソッドがないことは、この方法では実行できないことを意味します。
単体テスト:
[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());
}
属性はリクエストパイプラインを処理するときにフレームワークによって評価されるため、インメモリテストサーバーとの統合テストが必要になります。
統合テストでは、アプリケーションのコンポーネントが一緒に組み立てられたときに正しく機能することを確認します。 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の依存関係をスタブ/モックに置き換えるテスト専用のスタートアップを作成することもできます。
あなたができることは、匿名フィルターミドルウェアを追加するようにテストサーバーを設定することです:
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();
}