Mocking new Microsoft Entity Framework Identity Us

2020-02-02 13:30发布

问题:

Has anyone come up with a successful mocking solution for UserManager and RoleManager? I have been beating my head against a wall all day. All I want to do is mock the objects to use an in memory collection rather than hitting the Entity Framework data store. I've scoured the internet and tried several different approaches using MOQ.

I was under the impression that the new stuff was much easier to test. Am I missing something?

回答1:

Alternatively, you can mock the IUserStore<TUser> interface that UserManager accepts as an argument.

var userStore = new Mock<IUserStore<ApplicationUser>>();
var userManager = new UserManager(userStore.Object);

As @Joe Brunscheon notes in the comment below, UserManager detects support for other interfaces like IUserPasswordStore, etc. You can also moq those:

var passwordManager = userStore.As<IUserPasswordStore<ApplicationUser>>()
    .Setup(...).Returns(...);

You don't have to moq out all of these at once, you can just moq them up as needed by your code-under-test. In reality, the UserStore that EF uses to implement IUserStore implements other interfaces, and UserManager will do internal detection to see if these interfaces are implemented, and therefore, additional features supported. Fortunately, moq lets you mock up a surrogate that can implement many interfaces, using .As<T>().

In short, Microsoft.AspNet.Identity does give you everything you need to use it bare, without a wrapper, in your code. As long as you use dependency injection to instantiate your UserManager, you can safely moq it in unit tests by mocking up the interfaces it consumes and passing them via some kind of IUserStore<T> moq that is augmented to support methods on other interfaces internally detected by UserManager.



回答2:

I like to update the solution to this question for anyone who works on asp.net core:

    private Mock<UserManager<ApplicationUser>> GetMockUserManager()
    {
        var userStoreMock = new Mock<IUserStore<ApplicationUser>>();
        return new Mock<UserManager<ApplicationUser>>(
            userStoreMock.Object, null, null, null, null, null, null, null, null);
    }

Yes, 8 times null but so far there isn't any more graceful solution. If you are interested on the other parameters, take a look at the source code.



回答3:

You won't be able to Mock UserManager or RoleManager directly. What you CAN do, however, is mock an object that uses them.

For Example:

public interface IWrapUserManager
{
    UserManager WrappedUserManager {get; set;}
    //provide methods / properties that wrap up all UserManager methods / props.
}

public class WrapUserManager : IWrapUserManager
{
    UserManager WrappedUserManager {get; set;}
    //implementation here. to test UserManager, just wrap all methods / props.
}

//Here's a class that's actually going to use it.
public class ClassToTest
{
    private IWrapUserManager _manager;
    public ClassToTest(IWrapUserManager manager)
    {
        _manager = manager;
    }
    //more implementation here
}

On to the mocking:

[TestClass]
public class TestMock
{
    [TestMethod]
    public void TestMockingUserManager()
    {
        var mock = new Mock<IWrapUserManager>();
        //setup your mock with methods and return stuff here.
        var testClass = new ClassToTest(mock.Object); //you are now mocking your class that wraps up UserManager.
        //test your class with a mocked out UserManager here.
    }
}


回答4:

Just to expand on Rubito's answer, here is how I did it for RoleManager:

public static Mock<RoleManager<ApplicationRole>> GetMockRoleManager()
{
   var roleStore = new Mock<IRoleStore<ApplicationRole>>();
   return new Mock<RoleManager<ApplicationRole>>(
                roleStore.Object,null,null,null,null);

}


回答5:

public class FakeUserManager : UserManager<User>
    {
        public FakeUserManager() 
            : base(new Mock<IUserStore<User>>().Object,
                  new Mock<IOptions<IdentityOptions>>().Object,
                  new Mock<IPasswordHasher<User>>().Object,
                  new IUserValidator<User>[0],
                  new IPasswordValidator<User>[0],
                  new Mock<ILookupNormalizer>().Object, 
                  new Mock<IdentityErrorDescriber>().Object,
                  new Mock<IServiceProvider>().Object,
                  new Mock<ILogger<UserManager<User>>>().Object, 
                  new Mock<IHttpContextAccessor>().Object)
        { }

        public override Task<User> FindByEmailAsync(string email)
        {
            return Task.FromResult(new User{Email = email});
        }

        public override Task<bool> IsEmailConfirmedAsync(User user)
        {
            return Task.FromResult(user.Email == "test@test.com");
        }

        public override Task<string> GeneratePasswordResetTokenAsync(User user)
        {
            return Task.FromResult("---------------");
        }
    }