ASP.NET CoreでWeb APIを構築していますが、コントローラーの単体テストを行います。
簡単にモックできるデータアクセス用のインターフェイスを挿入します。しかし、コントローラーはトークンのリクエストのヘッダーをチェックする必要があり、コントローラーを自分でインスタンス化するだけではそのリクエストは存在しないように見えます。また、取得専用であるため、手動で設定することもできません。 ApiControllerをモックする多くの例を見つけましたが、それは.NETコアではありません。また、多くのチュートリアルと.netコアコントローラーの単体テスト方法の例がありますが、実際にHttpRequestを使用したものはありません。
これを実証するためにMCVEを作成しました。
[Produces("application/json")]
[Route("api/Players")]
public class PlayersController : Controller
{
private IAccessor accessor;
public PlayersController(IAccessor ac = null):base()
{
accessor = ac ?? AccessorFactory.GetAccessor();
}
/// <summary>
/// Get all players. Must be logged in.
/// </summary>
/// <returns>Ok or Unauthorized.</returns>
[HttpGet]
public IActionResult Get()
{
Player client = accessor.GetLoggedInPlayer(Request.Headers["token"]); // NRE here because Request is null
if (client == null) return Unauthorized();
return Ok(accessor.GetAllPlayers());
}
}
テストプロジェクトでMoqとMSTestを使用し、モックされたIAccessorを挿入します。リクエストを注入する方法、またはコントローラーで初期化する方法私の最後の手段は反射だと思いますが、私は本当にそれを避けたいです。
テスト対象のコントローラーのインスタンスを作成するときは、テストが完了するまでに必要な依存関係を含むHttpContext
を必ず割り当ててください。
HttpContext
をモックしてコントローラーに提供するか、フレームワークが提供するDefaultHttpContext
を使用してみてください。
//Arrange
var mockedAccessor = new Mock<IAccessor>();
//...setup mockedAccessor behavior
//...
var httpContext = new DefaultHttpContext(); // or mock a `HttpContext`
httpContext.Request.Headers["token"] = "fake_token_here"; //Set header
//Controller needs a controller context
var controllerContext = new ControllerContext() {
HttpContext = httpContext,
};
//assign context to controller
var controller = new PlayersController (mockedAccessor.Object){
ControllerContext = controllerContext,
};
//Act
var result = controller.Get();
//...
上記は、IAccessor
のようなコントローラーの依存関係を模擬する方法を既に知っていることを前提としており、テストに必要なフレームワーク固有の依存関係を提供する方法を示すことを目的としています。