10

I was wondering how would I go on about unit testing code that use IdentityUser without having to use UserManager (which needs to create a database, etc.). I am also using a Repository pattern with Unit of Work if that makes any difference..

If someone could provide me with more details and a simple example I would greatly appreciate it!

1 Answer 1

7

So if what you want is just some way to create and store IdentityUsers for unit tests, what you can do is create a TestUserStore and use the UserManager against that store.

public class MemoryUser : IUser {
    private readonly IList<UserLoginInfo> _logins;
    private readonly IList<Claim> _claims;
    private readonly IList<string> _roles;

    public MemoryUser(string name) {
        Id = Guid.NewGuid().ToString();
        _logins = new List<UserLoginInfo>();
        _claims = new List<Claim>();
        _roles = new List<string>();
        UserName = name;
    }

    public virtual string Id { get; set; }
    public virtual string UserName { get; set; }

    /// <summary>
    /// The salted/hashed form of the user password
    /// </summary>
    public virtual string PasswordHash { get; set; }

    /// <summary>
    /// A random value that should change whenever a users credentials have changed (password changed, login removed)
    /// </summary>
    public virtual string SecurityStamp { get; set; }

    public IList<UserLoginInfo> Logins { get { return _logins; } }

    public IList<Claim> Claims { get { return _claims; } }

    public IList<string> Roles { get { return _roles; } }
}

public class MemoryUserStore : IUserStore<MemoryUser>, IUserLoginStore<MemoryUser>, IUserRoleStore<MemoryUser>, IUserClaimStore<MemoryUser>, IUserPasswordStore<MemoryUser>, IUserSecurityStampStore<MemoryUser> {
    private Dictionary<string, MemoryUser> _users = new Dictionary<string, MemoryUser>();
    private Dictionary<UserLoginInfo, MemoryUser> _logins = new Dictionary<UserLoginInfo, MemoryUser>(new LoginComparer());

    public Task CreateAsync(MemoryUser user) {
        _users[user.Id] = user;
        return Task.FromResult(0);
    }

    public Task UpdateAsync(MemoryUser user) {
        _users[user.Id] = user;
        return Task.FromResult(0);
    }

    public Task<MemoryUser> FindByIdAsync(string userId) {
        if (_users.ContainsKey(userId)) {
            return Task.FromResult(_users[userId]);
        }
        return Task.FromResult<MemoryUser>(null);
    }

    public void Dispose() {
    }

    public IQueryable<MemoryUser> Users {
        get {
            return _users.Values.AsQueryable();
        }
    }

    public Task<MemoryUser> FindByNameAsync(string userName) {
        return Task.FromResult(Users.Where(u => u.UserName.ToUpper() == userName.ToUpper()).FirstOrDefault());
    }

    public Task AddLoginAsync(MemoryUser user, UserLoginInfo login) {
        user.Logins.Add(login);
        _logins[login] = user;
        return Task.FromResult(0);
    }

    public Task RemoveLoginAsync(MemoryUser user, UserLoginInfo login) {
        var logs = user.Logins.Where(l => l.ProviderKey == login.ProviderKey && l.LoginProvider == login.LoginProvider).ToList();
        foreach (var l in logs) {
            user.Logins.Remove(l);
            _logins[l] = null;
        }
        return Task.FromResult(0);
    }

    public Task<IList<UserLoginInfo>> GetLoginsAsync(MemoryUser user) {
        return Task.FromResult(user.Logins);
    }

    public Task<MemoryUser> FindAsync(UserLoginInfo login) {
        if (_logins.ContainsKey(login)) {
            return Task.FromResult(_logins[login]);
        }
        return Task.FromResult<MemoryUser>(null);
    }

    public Task AddToRoleAsync(MemoryUser user, string role) {
        user.Roles.Add(role);
        return Task.FromResult(0);
    }

    public Task RemoveFromRoleAsync(MemoryUser user, string role) {
        user.Roles.Remove(role);
        return Task.FromResult(0);
    }

    public Task<IList<string>> GetRolesAsync(MemoryUser user) {
        return Task.FromResult(user.Roles);
    }

    public Task<bool> IsInRoleAsync(MemoryUser user, string role) {
        throw new NotImplementedException();
    }

    public Task<IList<Claim>> GetClaimsAsync(MemoryUser user) {
        return Task.FromResult(user.Claims);
    }

    public Task AddClaimAsync(MemoryUser user, Claim claim) {
        user.Claims.Add(claim);
        return Task.FromResult(0);
    }

    public Task RemoveClaimAsync(MemoryUser user, Claim claim) {
        user.Claims.Remove(claim);
        return Task.FromResult(0);
    }

    public Task SetPasswordHashAsync(MemoryUser user, string passwordHash) {
        user.PasswordHash = passwordHash;
        return Task.FromResult(0);
    }

    public Task<string> GetPasswordHashAsync(MemoryUser user) {
        return Task.FromResult(user.PasswordHash);
    }

    public Task SetSecurityStampAsync(MemoryUser user, string stamp) {
        user.SecurityStamp = stamp;
        return Task.FromResult(0);
    }

    public Task<string> GetSecurityStampAsync(MemoryUser user) {
        return Task.FromResult(user.SecurityStamp);
    }

    public Task DeleteAsync(MemoryUser user) {
        throw new NotImplementedException();
    }

    public Task<bool> HasPasswordAsync(MemoryUser user) {
        return Task.FromResult(user.PasswordHash != null);
    }
}

var manager = new UserManager<MemoryUser>(new MemoryUserStore())
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks for the prompt reply! I am getting the following when trying to compile: Error 6 'WebApp.Tests.UnitTest1.MemoryUserStore' does not implement interface member 'System.IDisposable.Dispose()' D:\WebApp\WebApp\WebApp.Tests\UnitTest1.cs 23 22 WebApp.Tests Error 7 'WebApp.Tests.UnitTest1.MemoryUserStore' does not implement interface member 'Microsoft.AspNet.Identity.IUserStore<Microsoft.AspNet.Identity.EntityFramework.IdentityUser>.FindByNameAsync(string)' D:\WebApp\WebApp\WebApp.Tests\UnitTest1.cs 23 22 WebApp.Tests
You have to implement MemoryStore yourself.
@Hao Kung - could you please provide some more details on how to implement this (ie. what is between the {...} and how would I implement this in a Test)? For example, I'm trying to test a slightly modified "Task<ActionResult> Register(RegisterViewModel model)" in the AccountController and, as a relatively inexperienced MVC developer, I can't get it to work. Is there any documentation on how to specifically implement your suggestion using something like NSubstitute or Moq? Thanks!
Any thoughts on this? cannot convert from 'X.WebSite.MemoryUserStore' to 'Microsoft.AspNet.Identity.IUserStore<Microsoft.AspNet.Identity.EntityFramework.IdentityUser>'
@levelnis Actually UserManager only needs something implementing IUser. The out-of-box UserStore depends on IdentityUser. You need your own Userstore if you want to change that, which is exactly what this answer says.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.