有两种方法可以做到这一点。
模拟框架
UserManager 不是密封类。您可以使用 NSubstitute、Mocks 等现有框架将虚拟 UserManager 注入到您的调用代码中。大多数这些框架(我知道)支持模拟中非密封类的覆盖行为,只要该方法被标记为virtual,UserManager 上的几乎所有这些方法都有。这是一个如何使用 NSubstitute(我选择的框架)的示例。
var uManager = NSubstitute.Substitute.For<UserManager<MyUserType, int>>();
var result = new ClaimsIdentity();
uManager.CreateIdentityAsync(NSubstitute.Arg.Any<MyUserType>(), "login").Returns(Task.FromResult(result));
var controller = new MyAuthController(uManager);
controller.DoSomething();
代码重构
这是一种更复杂的方法,但从长远来看可能更好。
这是UserManager 的类签名(身份框架的第 2 版,但我想第 3 版对类本身没有任何重大更改,但可能略有不同)。
public class UserManager<TUser, TKey> : IDisposable where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
如您所述,该类未实现IUserManager 接口,但未密封,因此您可以创建自己的实现。您的实现可以实现一个接口,该接口可以指定原始UserManager<TUser, TKey> 类上的现有方法,然后您可以使用explicit implementation 实现该接口。
示例方法签名,然后是您的实现。
UserManager<TUser, TKey>上的方法
public virtual Task<ClaimsIdentity> CreateIdentityAsync(TUser user, string authenticationType)
界面上的方法(在本例中为强类型)。
Task<ClaimsIdentity> CreateIdentityAsync(MyUserType user, string authenticationType);
具体类的实现(在本例中为强类型)。
async Task<ClaimsIdentity> IMyUserTypeManager.CreateIdentityAsync(MyUserType user, string authenticationType)
{
return await this.CreateIdentityAsync(user, authenticationType);
}
使用您的控制器创建模拟与上面第一个示例中的说明相同。
这种方法的好处:
- 您现在已经更好地从实现代码中抽象出您正在使用 ASP.NET Identity 框架这一事实。这应该使将来更容易更新和/或更改 ASP.NET 标识。
- 您现在可以通过调用代码引用接口而不是具体类。这将使单元测试更容易,因为您可以更轻松地为这些接口创建模拟/替代。
这种方法的缺点是您必须创建和维护身份代码的代理,但通常您实际使用/调用的方法数量相当有限,因此工作量不大。