用于在MVC5中模拟ConfirmEmailAsync和其他UserManager方法的接口

时间:2016-07-19 16:31:13

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

我正在尝试对这种控制器方法进行单元测试,该方法在当前的MVC项目中开箱即用。

[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    if (userId == null || code == null)
    {
        return View("Error");
    }
    var result = await UserManager.ConfirmEmailAsync(userId, code);
    return View(result.Succeeded ? "ConfirmEmail" : "Error");
}

AccountController有一个构造函数,它将ApplicationUserManager和ApplicationSignInManager作为参数,以及与私有setter匹配的属性用于测试。但是,我无法弄清楚如何模拟ConfirmEmailAsync方法。

您可以在Identity名称空间中模拟各种接口:

var store = new Mock<IUserStore<ApplicationUser>>();

store.As<IUserEmailStore<ApplicationUser>>()
            .Setup(x => x.FindByIdAsync("username1"))
            .ReturnsAsync((ApplicationUser)null);

var mockManager = new ApplicationUserManager(store.Object);

AccountController ac = new AccountController(mockManager, null, GetMockRepository().Object, GetMockLogger().Object);

但是我无法找到或找出我需要哪个接口来创建ConfirmEmailAsync的模拟。

我该如何解决这个问题?作为参考,是否有一种很好的方法可以找出这些方法所使用的接口以进行模拟和测试?

2 个答案:

答案 0 :(得分:2)

ConfirmEmailAsync目前不是框架中接口的一部分。它位于UserManager<TUser, TKey>类中,它是Identity框架的基类。

我的解决方案?

摘要所有内容

我通过将身份的大部分功能抽象到自己的项目中来解决这个问题,这样我就可以更轻松地对其进行单元测试,并在其他项目中重用抽象。读完这篇文章后我明白了这个想法

<强> Persistence-Ignorant ASP.NET Identity with Patterns

然后我调整了这个想法以满足我的需要。我基本上只是将我需要的所有内容从asp.net.identity交换为我的自定义接口,这些接口或多或少地镜像了框架提供的功能,但具有更易于模拟的优点。

IIdentityUser

/// <summary>
///  Minimal interface for a user with an id of type <seealso cref="System.String"/>
/// </summary>
public interface IIdentityUser : IIdentityUser<string> { }
/// <summary>
///  Minimal interface for a user
/// </summary>
public interface IIdentityUser<TKey>
    where TKey : System.IEquatable<TKey> {

    TKey Id { get; set; }
    string UserName { get; set; }
    string Email { get; set; }
    bool EmailConfirmed { get; set; }
    string EmailConfirmationToken { get; set; }
    string ResetPasswordToken { get; set; }
    string PasswordHash { get; set; }
}

IIdentityManager

/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager : IIdentityManager<IIdentityUser> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser> : IIdentityManager<TUser, string>
    where TUser : class, IIdentityUser<string> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser, TKey> : IDisposable
    where TUser : class, IIdentityUser<TKey>
    where TKey : System.IEquatable<TKey> {

    Task<IIdentityResult> AddPasswordAsync(TKey userid, string password);
    Task<IIdentityResult> ChangePasswordAsync(TKey userid, string currentPassword, string newPassword);
    Task<IIdentityResult> ConfirmEmailAsync(TKey userId, string token);
    //...other code removed for brevity
}

IIdentityResult

/// <summary>
/// Represents the minimal result of an identity operation
/// </summary>
public interface IIdentityResult : System.Collections.Generic.IEnumerable<string> {
    bool Succeeded { get; }
}

在我的身份管理器的默认实现中,我只是包装ApplicationManager,然后在我的类型和asp.net.identity类型之间映射结果和功能。

public class DefaultUserManager : IIdentityManager {
    private ApplicationUserManager innerManager;

    public DefaultUserManager() {
        this.innerManager = ApplicationUserManager.Instance;
    }
    //..other code removed for brevity
    public async Task<IIdentityResult> ConfirmEmailAsync(string userId, string token) {
        var result = await innerManager.ConfirmEmailAsync(userId, token);
        return result.AsIIdentityResult();
    }
    //...other code removed for brevity
}

答案 1 :(得分:0)

免责声明:我在Typemock工作。

实际上,如果您使用Typemock,则不需要任何界面,只需要伪造您需要的IdentityResult并更改异步方法的行为&#34; ConfirmEmailAsync&#34;,for例如,检查未确认电子邮件的方案的测试:

workspace/test/target/classes/login_commands.json