首页 > 解决方案 > 如何测试将身份与只读属性一起使用的控制器?

问题描述

我有以下代码:

[Route("resources/avatar")]
[ApiController]
public class AvatarController : ControllerBase
{
    private readonly ApplicationDbContext database;
    private readonly IWebHostEnvironment environment;
    private readonly IUserManagerWrapper userManagerWrapper;

    public AvatarController(IUserManagerWrapper userManagerWrapper, IWebHostEnvironment environment,
        ApplicationDbContext database)
    {
        this.userManagerWrapper = userManagerWrapper;
        this.environment = environment;
        this.database = database;
    }

    [HttpGet]
    [Route("")]
    public async Task<IActionResult> Index()
    {
        if (User == null) return DefaultImage();

        var user = await this.userManagerWrapper.GetUserAsync(User);
        if ((user?.Avatar?.Length ?? 0) == 0) return DefaultImage();

        return File(user.Avatar, "image/jpeg");
    }
}

我有一个测试这个问题Index Page

Useris a propertythat 来自ControllerBase并且是 type ClaimsPrincipal

我使用了一个wrapper我会包装的地方usermanager,然后使用interface我会的一个mock

这种方法的问题是我无法将其设置ClaimsPrincipal为,null因为它是一个read-only.

这是我的测试:

[TestFixture]
public class AvatarController_Should
{
    [Test]
    public async Task IndexReturnsDefaultImage()
    {
        var hostingEnvironmentMock = new Mock<IWebHostEnvironment>();
        var dabataseName = nameof(IndexReturnsDefaultImage);
        var options = AvatarTestUtil.GetOptions(dabataseName);
        var userManagerWrapperMock = new Mock<IUserManagerWrapper>();

        using (var actAndAssertContext = new ApplicationDbContext(options))
        {
            var sut = new AvatarController(userManagerWrapperMock.Object, hostingEnvironmentMock.Object, actAndAssertContext);
        }
     }
}
   public class AvatarTestUtil
   {
    public static DbContextOptions<ApplicationDbContext> GetOptions(string databaseName)
    {
        var serviceCollection = new ServiceCollection()
            .AddEntityFrameworkInMemoryDatabase()
            .BuildServiceProvider();

        return new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName)
            .UseInternalServiceProvider(serviceCollection)
            .Options;
    }
}

}

我愿意使用全新的方法。

这就是我以前做测试的方式identity,但我现在卡住了。

标签: c#unit-testingidentity

解决方案


查看 ControllerBase 的源代码,我们可以看到 User 是这样定义的

public ClaimsPrincipal User => HttpContext?.User;

所以User实际上来自HttpContext。但是 HttpContext 也是只读的。但是,深入挖掘源代码,我们可以看到 HttpContext 是从 ControllerContext 派生的

public HttpContext HttpContext => ControllerContext.HttpContext;

唉! ControllerContext 在具体实现中其实有一个setter!

public ControllerContext ControllerContext { get; set; }

如果需要,我们可以在这里设置一个全新的 ControllerContext。但我们真的只需要 ControllerContext.User。幸运的是,它也有一个二传手。由于您只需要设置 User,因此我们可以直接在此处进行设置,而无需新建另一个 ControllerContext。

using (var actAndAssertContext = new ApplicationDbContext(options))
{
     var sut = new AvatarController(userManagerWrapperMock.Object, hostingEnvironmentMock.Object, actAndAssertContext);
     sut.ControllerContext.HttpContext.User = null;
}

推荐阅读