如何模拟 UserManager<IdentityUser>

How to mock UserManager<IdentityUser>

我正在尝试研究如何将单元测试添加到我的项目中。我认为最好从一个空白项目开始,然后从头开始解决,而不是将其添加到我的主项目中。一旦我理解了这个过程,我想我可以开始重构我的项目以添加测试。

网络应用程序

所以我创建了一个 Web 应用程序并向其添加了默认用户身份。

这让我开始看起来像这样

public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<IdentityUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

然后我创建了一个简单的控制器,并在构造函数中传递了用户管理器。

[Route("api/[controller]")]
[ApiController]
public class SweetController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;

    public SweetController(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }
    public async Task<int> NumberOfGummyBearsForUser(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
        var userHasASweetTooth = await _userManager.IsInRoleAsync(user, "SweetTooth");
        if (userHasASweetTooth)
        {
            return 100;
        }
        else
        {
            return 1;
        }
    }
}

单元测试

我一直试图做的第一件事是模拟这个用户管理器,但它不起作用。

public void Test1()
    {
        // Arrange
        var mockUser = new Mock<UserManager<IdentityUser>>();
        var userManager = new UserManager(mockRepo.Object);  <-- error here see image below

        var controller = new SweetController(userManager.Object);

        // Act
        var result = await controller.NumberOfGummyBearsForUser("123");

        // Assert
        Assert.Equal(100, result);
    }

错误看起来像这样

我想我需要传递更多信息来创建这个 usermanager 对象,但我不确定我找到的所有教程都使用 ApplicationUser 而不是 IdentityUser,所以我不知道如何模拟这个对象。

你只要做

// Arrange
var mockUser = new Mock<UserManager<IdentityUser>>();

var controller = new SweetController(mockUser.Object);

你不需要

var userManager = new UserManager(mockRepo.Object);  <-- error here see image below

完全没有。 mockUser 已经是模拟的 UserManager<T>,您可以通过 mock.Object.

放置一个模拟实例

当你模拟一个对象时,你不必用它的所有依赖项来实例化它(那将是集成测试),这就是模拟的重点(以及使方法 return 成为所需的值和进行行为测试以确保您的测试代码调用了具有模拟对象的特定参数的特定方法。

当然,上面的代码本身是行不通的,因为您没有为 FindByIdAsyncIsInRoleAsync 设置任何测试 conditions/returns。您必须使用

设置这些
mockUser.Setup( userManager => userManager.FindByIdAsync(It.IsAny<string>()))
    .ReturnsAsync(new IdentityUser { ... });
mockUser.Setup( userManager => userManager.IsInRoleAsync(It.IsAny<IdentityUser>(), "SweetTooth"))
    .ReturnsAsync(true);

然后,每当调用模拟时,它return就是您的预定义用户和预定义结果。

在 Tseng 的帮助下,我完成了这项工作。一个完整的工作版本是

控制器

   private readonly UserManager<IdentityUser> _userManager;

    public SweetController(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task<IdentityUser> GetUser(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
        return user;
    }

测试

[Fact]
    public async Task Test1()
    {
        // Arrange
        var store = new Mock<IUserStore<IdentityUser>>();
        store.Setup(x => x.FindByIdAsync("123", CancellationToken.None))
            .ReturnsAsync(new IdentityUser()
            {
                UserName = "test@email.com",
                Id = "123"
            });

        var mgr = new UserManager<IdentityUser>(store.Object, null, null, null, null, null, null, null, null);

        var controller = new SweetController(mgr);

        // Act
        var result = await controller.GetUser("123");

        // Assert
        Assert.NotNull(result);
        Assert.Equal("123", result.Id);
    }

我删除了角色检查只是为了让它尽可能基本地工作。