【问题标题】:How to mock UserManager<IdentityUser>如何模拟 UserManager<IdentityUser>
【发布时间】:2019-08-20 02:52:45
【问题描述】:

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

网络应用程序

所以我创建了一个 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,所以我不知道如何模拟这个对象。

【问题讨论】:

  • 你不能只传递var userManager = mockRep.Object,因为它是 userManager 的模拟吗?
  • MockHelpers 来自身份测试本身可能是一个很好的参考点。
  • UserManager&lt;T&gt; 是你想要模拟的对象时,你为什么要new 向上UserManager&lt;T&gt; 不清楚?
  • @Tseng 好吧,我也不确定,因为我说过我正在尝试学习如何做到这一点,我正在关注 alastairchristian.com/… 如果您有更好的方法,请随时添加 anwser
  • mockUser 已经是 UserManager&lt;T&gt; 的模拟。你将它传递给 controller(显然你想测试控制器,NOT UserManager,ASP.NET Core Identity 团队已经为你做了:P

标签: c# asp.net-core asp.net-identity moq xunit


【解决方案1】:

你就是这样做

// 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&lt;T&gt;,您可以通过mock.Object 放置一个模拟实例。

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

当然,上述代码本身不起作用,因为您没有为FindByIdAsyncIsInRoleAsync 设置任何测试条件/返回。您必须使用

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

然后,每当调用 mock 时,它都会返回您的预定义用户和预定义结果。

【讨论】:

  • 好吧,我想我想多了。关于将数据输入 usermnager 以便我对其进行测试的任何提示?
  • 您没有将任何数据“输入”到管理器中,而是设置了条件(如上面的条件)。当使用 any 用户和“SweetTooth”作为第二个参数调用方法“IsInRoleAsync`”时,它返回 true。模拟的重点是您不需要完全初始化模拟对象,而只需告诉:“在传递或调用某些内容时返回此值”。在模拟时不会调用 UserManager 中的任何代码(除非您启用基类的调用,但由于未完全初始化,这在大多数情况下不起作用类)
  • 你试过运行这个吗?我在运行测试时收到“Castle.DynamicProxy.InvalidProxyConstructorArgumentsException”
  • 不,我在工作:P 请参阅 Kirkins 链接评论,您需要将匹配参数的构造函数传递给模拟选项,因为第一个不能为空,它必须是模拟本身:github.com/aspnet/AspNetCore/blob/release/2.2/src/Identity/test/… 您可以直接引用它(如果您不介意测试项目中的额外依赖项),或者以同样的方式进行。或者使用AutoMoq(Autofac moq 扩展)之类的东西
  • 不过,Automoq 不会帮助您解决起订量依赖问题,前段时间也遇到过类似问题
【解决方案2】:

在 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);
    }

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

【讨论】:

    猜你喜欢
    • 2019-08-16
    • 2016-03-26
    • 2020-01-26
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多