-->

How to mock UserManager

2019-06-27 07:54发布

问题:

I am trying to work out how to add unit testing to my project. I thought it was best to start with a blank project and work it out from the start rather than adding it to my main project. Once I understand the process i figure i can start refactoring my project for adding testing.

web application

So i created a web application and added default user identity to it.

This gave me a start up looking like this

public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<IdentityUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

Then i created a simple controller and passed the usermanager in the constructor.

[Route("api/[controller]")]
[ApiController]
public class SweetController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;

    public SweetController(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }
    public async Task<int> NumberOfGummyBearsForUser(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
        var userHasASweetTooth = await _userManager.IsInRoleAsync(user, "SweetTooth");
        if (userHasASweetTooth)
        {
            return 100;
        }
        else
        {
            return 1;
        }
    }
}

Unit Test

The first thing i have been trying to do is mock this user manager but its not working.

public void Test1()
    {
        // Arrange
        var mockUser = new Mock<UserManager<IdentityUser>>();
        var userManager = new UserManager(mockRepo.Object);  <-- error here see image below

        var controller = new SweetController(userManager.Object);

        // Act
        var result = await controller.NumberOfGummyBearsForUser("123");

        // Assert
        Assert.Equal(100, result);
    }

The error looks like this

I think i need to pass more to create this usermanager object but i am not sure what all the tutorials i have found use ApplicationUser and not IdentityUser so i am at a loss as to how i can mock this object.

回答1:

You just do

// Arrange
var mockUser = new Mock<UserManager<IdentityUser>>();

var controller = new SweetController(mockUser.Object);

You don't need

var userManager = new UserManager(mockRepo.Object);  <-- error here see image below

at all. mockUser is already the mocked UserManager<T>, which you place a mocked instance via mock.Object.

When you mock an object you don't have to instantiate it with all of its dependencies (that would be integration test), that's the point of mocking (along with making methods return a desired value and do behavior tests to make sure your tested code has called a specific method with specific parameter of the mocked object).

Of course per se the above code won't work, since you didn't setup any test conditions/returns for FindByIdAsync and IsInRoleAsync. You have to setup these with

mockUser.Setup( userManager => userManager.FindByIdAsync(It.IsAny<string>()))
    .ReturnsAsync(new IdentityUser { ... });
mockUser.Setup( userManager => userManager.IsInRoleAsync(It.IsAny<IdentityUser>(), "SweetTooth"))
    .ReturnsAsync(true);

Then whenever the mock is called it returns your predefined user and a predefined result.



回答2:

With Tseng's help I got this working. A fully working version of this is

controler

   private readonly UserManager<IdentityUser> _userManager;

    public SweetController(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task<IdentityUser> GetUser(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
        return user;
    }

Test

[Fact]
    public async Task Test1()
    {
        // Arrange
        var store = new Mock<IUserStore<IdentityUser>>();
        store.Setup(x => x.FindByIdAsync("123", CancellationToken.None))
            .ReturnsAsync(new IdentityUser()
            {
                UserName = "test@email.com",
                Id = "123"
            });

        var mgr = new UserManager<IdentityUser>(store.Object, null, null, null, null, null, null, null, null);

        var controller = new SweetController(mgr);

        // Act
        var result = await controller.GetUser("123");

        // Assert
        Assert.NotNull(result);
        Assert.Equal("123", result.Id);
    }

I removed the role check just to get it working as basic as possible.