【问题标题】:How to mock UserManager in .NET Core with NUnit?如何使用 NUnit 在 .NET Core 中模拟 UserManager?
【发布时间】:2018-06-16 15:52:45
【问题描述】:

我想使用 Razor Pages 对我在 .NET Core 中制作的应用程序进行单元测试。我需要模拟我的dbContext 和我的UserManager - 但是,我似乎不断收到null 作为结果。

FakeUserManager

public class FakeUserManager : UserManager<ApplicationUser>
{
  public FakeUserManager()        
      : base(new Mock<IUserStore<ApplicationUser>>().Object,
         new Mock<IOptions<IdentityOptions>>().Object,
         new Mock<IPasswordHasher<ApplicationUser>>().Object,
         new IUserValidator<ApplicationUser>[0],
         new IPasswordValidator<ApplicationUser>[0],
         new Mock<ILookupNormalizer>().Object,
         new Mock<IdentityErrorDescriber>().Object,
         new Mock<IServiceProvider>().Object,
         new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
    { }
}

我的测试类

 [TestFixture]
 public class UserTest
 {
     private ApplicationDbContext _dbContext { get; set; }
     public void TestSetUp()
     {
         DbContextOptionsBuilder<ApplicationDbContext> optionsbuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
         optionsbuilder.UseInMemoryDatabase(databaseName: "TestDB");
         _dbContext = new ApplicationDbContext(optionsbuilder.Options);
         _dbContext.Users.Add(new ApplicationUser
         {
             Id = "1",
             FirstName = "Test",
             LastName = "Alpha",
             CreatedAt = DateTime.Now,
             UpdatedAt = DateTime.Now,
         });
         _dbContext.SaveChanges();
     }

     [Test]
     public void MyTest()
     {
         Mock<FakeUserManager> fakeUserManager = new Mock<FakeUserManager>();
         var result = fakeUserManager.Object.FindByIdAsync("1");

         // I know this Assert always returns  success, but I'd like var result to return my ApplicationUser found by the FakeUserManager
         Assert.AreEqual("test", "test");
     }
 }

在将此问题标记为重复之前,是的 - 我会查看其他帖子,但它们不满足我的回答。

当我运行这个测试时,var result 返回null,我不明白为什么。

编辑###

    [Test]
    public async void MyTest() 
    {
        var mockStore = Mock.Of<IUserStore<ApplicationUser>>();
        var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore, null, null, null, null, null, null, null, null);

        var user = new ApplicationUser
        {
            Id = "1",
            FirstName = "Test",
            LastName = "Test"
        };

        mockUserManager
            .Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>()))
            .ReturnsAsync(IdentityResult.Success);

        await mockUserManager.Object.CreateAsync(user);

        var result = mockUserManager.Object.FindByIdAsync("UserA");

        Assert.AreEqual("test", "test");


    }

【问题讨论】:

  • 模拟上没有设置,所以任何被调用的成员都将为空。其次,你制作一个假经理,然后嘲笑这个假经理。感觉就像XY problem,最后你不应该测试你无法控制的代码。听起来您与实现问题紧密耦合。
  • 我明白你的意思。我对 .NET 中的单元测试不太熟悉。我在 mockUserManager 上添加了设置,但它仍然返回 null

标签: c# unit-testing asp.net-core asp.net-core-identity


【解决方案1】:

您不应该测试 UserManager 是否可以从数据库中检索数据(这是超出您范围的框架的内部问题)。相反,您应该像这样模拟 UserManager 的 FindByIdAsync 功能:

var UserStoreMock = Mock.Of<IUserStore<AppUser>>();
var userMgr = new Mock<UserManager<AppUser>>(UserStoreMock, null, null, null, null, null, null, null, null);
var user = new AppUser() { Id = "f00", UserName = "f00", Email = "f00@example.com" };
var tcs = new TaskCompletionSource<AppUser>();
tcs.SetResult(user);
userMgr.Setup(x => x.FindByIdAsync("f00")).Returns(tcs.Task);

为了您的身份设置,以确保具有适当权限的经过身份验证的用户正在拨打电话:

Mock<ClaimsPrincipal> ClaimsPrincipalMock = new Mock<ClaimsPrincipal>();
ClaimsPrincipalMock.Setup(x => x.IsInRole("Admin")).Returns(true);
ClaimsPrincipalMock.Name = "SomeAdmin";
ClaimsPrincipalMock.SetupGet(x => x.Identity.Name).Returns(ClaimsPrincipalMock.Name);

然后为您的帐户控制器设置上下文,将您的 UserManager 作为 DI 参数之一传递:

AccountController SUT = new AccountController(userMgr);
var context = new ControllerContext();
context.HttpContext = new DefaultHttpContext();
context.HttpContext.User = ClaimsPrincipalMock.Object;
SUT.ControllerContext = context;

最后,在您的控制器中,匹配的代码将类似于:

[HttpGet("User")]
public async Task<IActionResult> GetUser(string id)
{
    var user = await _userManager.FindByIdAsync(id);     
    bool isAdmin= User.IsInRole("Admin");

    if (user == default(AppUser) || (User.Identity.Name != user.UserName && isAdmin == false))
    {
         return NotFound();
    }

    return Ok(user);
}

【讨论】:

  • 我同意这种做法。就像@Nkosi 提到的那样,通常不需要模拟整个 UserManager,尽管有时可能会依赖 UserManager 并且在您实际测试的对象中需要基本的 UserStore 功能。这是完成工作的良好组合。
猜你喜欢
  • 2018-08-16
  • 2021-09-12
  • 2021-03-25
  • 2016-03-26
  • 2019-08-20
  • 2018-04-05
  • 2020-11-27
  • 2021-11-16
相关资源
最近更新 更多