How can i mock i base method when test derived class

Viewed 1170

I have the problem that i want to write a unit test of a custom function in my user service. But this user service derives from asp.net core identity user manager. I want to extend the functionality of the user manager therefore i derived from it. So I want to test the method CreateCRohMUserAsync

    public class UserService : UserManager<User>, IUserService
{
    public UserService(IUserStore<User> store,
        IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<User> passwordHasher,
        IEnumerable<IUserValidator<User>> userValidators,
        IEnumerable<IPasswordValidator<User>> passwordValidators,
        ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<User>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {
    }

    public async Task<IdentityResult> CreateCRohMUserAsync(User user)
    {
        PasswordGenerator pwGenerator = new PasswordGenerator();
        var password = pwGenerator.Generate();

        user.UserName = GetUniqueUserName(user.FirstName, user.LastName);

        var result = await base.CreateAsync(user, password);

        if (result.Succeeded)
        {
            //TODO: send mail to created user                
        }

        return result;
    }


    public string GetUniqueUserName(string firstName, string lastName)
    {
        if (string.IsNullOrEmpty(firstName))
        {
            throw new ArgumentNullException(nameof(firstName), "firstName can not be null");
        }
        if (string.IsNullOrEmpty(lastName))
        {
            throw new ArgumentNullException(nameof(lastName), "lastName can not be null");
        }

        return lastName + firstName.Substring(0, 2) + Users.Count() + 1;
    }
}

}

In my unit test i want to mock the base.CreateAsync(user,password) and the Users.Count() functionality of my base class. But I don't know how to do this. Or is my implementation wrong? How can I unit test this mehtod?

2 Answers

Asumming you're using moq.

First you'll have to remove the base keyword in CreateCRohMUserAsync:

var result = await CreateAsync(user, password); // Previously base.CreateAsync

If it's present, the base class's implementation is always called - mocking CreateAsync will have no effect (moq mocks methods by overriding them).

After removing base, you can test CreateCRohMUserAsync like this:

[Fact]
public async void CreateCRohMUserAsync_DoesSomething()
{
    // Arrange
    ...
    Mock<UserService> mockTestSubject = new Mock<UserService>(...); // Pass services you'd normally pass to the UserService constructor
    mockTestSubject.CallBase = true; // So when you call CreateCRohMUserAsync, the logic you defined runs
    mockTestSubject.Setup(t => t.CreateAsync(...)).ReturnsAsync(...); // Mock CreateAsync
    // If GetUniqueUserName is virtual, you can mock it too    
    // mockTestSubject.Setup(t => t.GetUniqueUserName(...)).Returns(...);

    // Act
    IdentityResult result = await mockTestSubject.Object.CreateCRohMUserAsync(...);

    // Assert
    ...
}

The same pattern will work for GetUniqueUserName.

Essentially:

  • Mock your derived class so you can setup virtual members of your base class.
  • Set CallBase to true so you can run logic you defined in your derived class.

As per your example code, I'd recommend you change the inheritance to composition as you only use methods from the base class, not override any.

interface IUserManager
{
    IdentityResult CreateAsync();
}

class DefaultUserManager : IUserManager
{
    public DefaultUserManager(UserManager<User> manager)
    {
    }

    public IdentityResult CreateAsync()
    {
        // Call UserManager<User>
    }
}

public class UserService : IUserService
{
    private readonly IUserManager _userManager;

    public UserService(IUserManager userManager, ...)
    {
        _userManager = userManager;
    }

    public async Task<IdentityResult> CreateCRohMUserAsync(User user)
    {
        var result = await _userManager.CreateAsync();
        return result;
    }
}

I would abstract out the use of UserManager<User> into an interface called IUserManager, and the implementation still uses UserManager<User>. Your UserService however calls the interface, so that in your unit test, you could simply provide a mocked instance of IUserManager and avoid calling UserManager<User>.

Related