模拟ApplicationUserManager用于单元测试MVC控制器

时间:2016-06-29 22:47:19

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

我想知道是否有人可以帮助我。

我正在为特定的控制器编写单元测试。该控制器继承自BaseController,BaseController具有以下属性:

private ApplicationUserManager userManager;
public ApplicationUserManager UserManager 
{ 
    get { return this.userManager ??  this.Request.GetOwinContext().GetUserManager<ApplicationUserManager>(); } 
    set { this.userManager = value; } 
}

ApplicationUserManager的ctor是:

public ApplicationUserManager(IUserStore<ApplicationUser> store, IIdentityMessageService emailService)
        : base(store)
    {
        this.EmailService = emailService;

        var dataProtectionProvider = Startup.DataProtectionProvider;
        this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
    }

这就是我在模拟ApplicatonUserManager类时所做的:

var store = new Mock<IUserStore<ApplicationUser>>();
var emailService = new Mock<IIdentityMessageService>();
var applicationUserManager = new Mock<ApplicationUserManager>(store.Object, emailService.Object);
this.targetController.UserManager = applicationUserManager.Object;
var dataprotectionprovided = new Mock<IDataProtectionProvider>();
applicationUserManager.Setup(r => r.UserTokenProvider).Returns(new DataProtectorTokenProvider<ApplicationUser, string>(dataprotectionprovided.Object.Create("ASP.NET Identity")));
this.targetController.UserManager = applicationUserManager.Object;

我试图模仿这个,但因为这不是虚拟属性(UserTokenProvider),它不允许我,我得到这个例外:

System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: r => r.UserTokenProvider

任何人都可以帮我解决这个问题吗?我只是想模仿这个来测试从具有该属性的BaseController继承的控制器..

由于

2 个答案:

答案 0 :(得分:1)

感谢您的帮助@bwyn

我设法用你的建议破解它。刚刚为ApplicationUserManager创建了一个新的构造函数,如下所示:

public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store)
    {
    }

然后是单元测试:

var user = new ApplicationUser { User = new User { UserId = 1 } };

        var store = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict);
        store.As<IUserStore<ApplicationUser>>().Setup(x => x.FindByIdAsync(It.IsAny<string>())).ReturnsAsync(user);
        this.targetController.UserManager = new ApplicationUserManager(store.Object);

谢谢大家!

答案 1 :(得分:0)

正如你的答案中的一条评论所指出的,创建一个额外的构造函数可能会导致另一个开发人员使用错误的构造函数。

我可以建议你尝试以下方法而不是这个。

将原始ApplicationUserManager构造函数中的代码移动到新的受保护虚拟方法中。然后修改现在空的构造函数以调用新的虚方法。

public class ApplicationUserManager
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store, IIdentityMessageService emailService)
: base(store)
    {
        CalledAfterConstruction();
    }

    protected virtual void CalledAfterConstruction()
    {
        this.EmailService = emailService;

        var dataProtectionProvider = Startup.DataProtectionProvider;
        this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
    }
}

现在,在继承ApplicationUserManager的单元测试时创建一个新的MockApplicationUserManager类,只用一个空方法覆盖继承的'CalledAfterConstruction',你几乎可以像在答案中那样做。

public class MockClass : ApplicationUserManager
{
    public MockClass(IUserStore<ApplicationUser> store, IIdentityMessageService emailService) : base(store, emailService)
    {
    }

    protected override void CalledAfterContruction()
    {

    }
}

是的,这有点复杂,但它确实阻止了人们滥用原来的课程