ASP.NET Core控制器中的模拟HttpRequest

53
我正在使用 ASP.NET Core 构建 Web API,并希望对控制器进行单元测试。

我注入了一个数据访问接口,可以轻松地模拟。但是控制器必须检查请求中的标头是否有令牌,当我手动实例化控制器时,该请求似乎并不存在,而且它也是只读的,所以我甚至无法手动设置它。我发现了许多关于如何模拟 ApiController 的示例,但那不是 .NET core。也有很多关于如何单元测试 .net core 控制器的教程和示例,但没有一个实际使用 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,并注入了一个被mock的IAccessor。我该如何注入请求,或通过控制器初始化它?我猜最后的办法是反射, 但我真的想避免这种情况。


可能是模拟HttpContext以对.NET Core MVC控制器进行单元测试?的重复问题。 - Michael Freidgeim
1个回答

85

创建控制器实例时,确保分配一个包含所需依赖项的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 ,并旨在演示如何提供测试所需的特定于框架的依赖项。


我也听说过在测试中不应该模拟http-context。因此,我想强调建议使用DefaultHttpContext而不是尝试模拟它。 - Ravior
DefaultHttpContext是内部的,不能使用。 - undefined
根据文档显示,它仍然是一个公共密封类。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接