Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock Asp.net identity UserManager's CreateAsync method

Tags:

c#

asp.net

moq

I am trying to mock the UserStore's CreateAsync method to have an in-memory user store.

public void Can_Create_User()
{
    //Arrange
    var dummyUser = new ApplicationUser() { UserName = "PinkWarrior", Email = "[email protected]" };
    var mockStore = new Mock<IUserStore<ApplicationUser>>();

    var userManager = new UserManager<ApplicationUser>(mockStore.Object);
    mockStore.Setup(x => x.CreateAsync(dummyUser))
                .Returns(Task.FromResult(IdentityResult.Success));


    //Act
    Task<IdentityResult> tt = (Task<IdentityResult>)mockStore.Object.CreateAsync(dummyUser);
    var user = userManager.FindByName("PinkWarrior");

    //Assert
    Assert.AreEqual("PinkWarrior", user.UserName);
}

The user above is always null, because I'm never inserting it . So the problem I have is how can I make userManager,which isn't a mock return a user from my mock store?

This answer was somewhat similar but doesn't answer my question here.

Any help greatly appreciated

like image 373
iAteABug_And_iLiked_it Avatar asked Feb 21 '15 11:02

iAteABug_And_iLiked_it


3 Answers

Check the MockHelper.cs class from tests in Identity git repo

Identity GitHub repo link or Closed Issue for mocking

You should find inspirations regarding mocks there, for example

  public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store = null) where TUser : class
    {
        store = store ?? new Mock<IUserStore<TUser>>().Object;
        var options = new Mock<IOptions<IdentityOptions>>();
        var idOptions = new IdentityOptions();
        idOptions.Lockout.AllowedForNewUsers = false;
        options.Setup(o => o.Value).Returns(idOptions);
        var userValidators = new List<IUserValidator<TUser>>();
        var validator = new Mock<IUserValidator<TUser>>();
        userValidators.Add(validator.Object);
        var pwdValidators = new List<PasswordValidator<TUser>>();
        pwdValidators.Add(new PasswordValidator<TUser>());
        var userManager = new UserManager<TUser>(store, options.Object, new PasswordHasher<TUser>(),
            userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
            new IdentityErrorDescriber(), null,
            new Mock<ILogger<UserManager<TUser>>>().Object,
            null);
        validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>()))
            .Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
        return userManager;
    }

im using it with this method :

       private UserController BuildCoontrollerWithDatabase()
    {
        DbContextOptionsBuilder dbContextOptionsBuilder = new DbContextOptionsBuilder();
        dbContextOptionsBuilder.UseInMemoryDatabase();

        ApplicationDbContext applicationDbContext = new ApplicationDbContext( dbContextOptionsBuilder.Options);

        var userStore = new UserStore<ApplicationUser>(applicationDbContext);

        UserManager<ApplicationUser>  userManager = TestUserManager<ApplicationUser>(userStore);

        return new UserController(userManager);


    }
like image 128
Michał W. Avatar answered Nov 09 '22 11:11

Michał W.


You need to mock the call to FindByNameAsync, because that is what UserManager will call:

public void Can_Create_User()
{
    //Arrange
    var dummyUser = new ApplicationUser() { UserName = "PinkWarrior", Email = "[email protected]" };
    var mockStore = new Mock<IUserStore<ApplicationUser>>();

    var userManager = new UserManager<ApplicationUser>(mockStore.Object);
    mockStore.Setup(x => x.CreateAsync(dummyUser))
                .Returns(Task.FromResult(IdentityResult.Success));

    mockStore.Setup(x => x.FindByNameAsync(dummyUser.UserName))
                .Returns(Task.FromResult(dummyUser));


    //Act
    Task<IdentityResult> tt = (Task<IdentityResult>)mockStore.Object.CreateAsync(dummyUser);
    var user = userManager.FindByName("PinkWarrior");

    //Assert
    Assert.AreEqual("PinkWarrior", user.UserName);
}

However, I believe with such test you are testing Microsoft.AspNet.Identity.UserManager and not your code. What you are proving is that UserManager somehow calls your mockStore.

Source code for Microsoft.AspNet.Identity contains these type of tests.

like image 22
milanio Avatar answered Nov 09 '22 11:11

milanio


Your mocked user store is not prepared correctly. As it is a mock, you shouldn't expect that mockStore.Object.CreateAsync will do anything, i.e. it's mot going to add anything anywhere.

You need to prepare the corresponding "read" operation, as most probably the user manager will invoke it. (setup the FindByNameAsync method on the mock to return the identity you need).

In any case what you are trying to do is pointless, as you are trying to test a framework class UserManager.

Probably what you need actually is to mock the user manager itself, and use that mock in your controller, or wherever you use that user manager.

like image 2
Sunny Milenov Avatar answered Nov 09 '22 11:11

Sunny Milenov