Question

I'm trying to learn about Unit Testing with the new VS2013 Default MVC w/ Authentication Project. One of the first things I want to test is registering a user. (I know I probably don't need to unit test this since it's already MS tested code but I want to use this to understand the basics). I've also heard that the new Membership code is more 'testable' so I don't need to create my own membership interfaces, etc...

I'm using NSubstitute as a faking framework.

Looking at the Account Controller -> Register() async method

namespace WebApplication1.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        public AccountController()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
        }

        public AccountController(UserManager<ApplicationUser> userManager)
        {
            UserManager = userManager;
        }

        public UserManager<ApplicationUser> UserManager { get; private set; }

        ...


       //
       // POST: /Account/Register
       [HttpPost]
       [AllowAnonymous]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Register(RegisterViewModel model)
       {
           if (ModelState.IsValid)
           {
                var user = new ApplicationUser() { UserName = model.UserName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                   AddErrors(result);
                }
           }

           // If we got this far, something failed, redisplay form
           return View(model);
       }

If I wanted to write a simple test (ex. Register_RegisterValidUser), how would I do this? I know I need to substitute for the UserManager somehow but this did not work for me:

var substitute = Substitute.For<UserManager<ApplicationUser>>();

I also understand that I need to bypass the async Task<> functions using Task.FromResult but I'm not sure how to return a valid objects from the CreateAsync() and the SigninAsync() methods.

Can somebody help with some sample test code? Many thanks!

Was it helpful?

Solution

To mock the user manager with NSubstitute you have to use this:

var userStore = Substitute.For<IUserStore<ApplicationUser>>();
var userManager = Substitute.For<UserManager<ApplicationUser>>(userStore);

Now you can fake the methods result too. For example:

userManager.FindByNameAsync(Arg.Any<string>()).Returns(Task.FromResult(new ApplicationUser()));

Hope this help you.

OTHER TIPS

Test Guidance

You can Unit Test couple of behaviours, but I will just show you the direction for a single Unit test that just verify whether receive the correct RedirectToAction value. For example,

return RedirectToAction("Index", "Home");

Improving the Testability

In your question you mentioned

I've also heard that the new Membership code is more 'testable' so I don't need to create my own membership interfaces, etc..

While this is true, I would make a small adjustment so we can make your implementation bit more testable. This way your Unit Test can purely concentrate on the specific behaviour your going test. In other words we can make you SUT (System Under Test) more testable.

  await SignInAsync(user, isPersistent: false);

I believe SignInAsync is a private method. There should be some behavior in this method you can probably extract out to a seperate implementation which you can inject in to the SUT. We can call this ISignInManager

public interface ISignInManager {
    Task SignInAsync(ApplicationUser user, bool isPersistent);
}

The benefit of this is that now you can inject the behaiovr of an ISignInManager to perform the SignIn tasks and your SUT become more testable. You should see that there will be less mocking/stubbing in your Unit test and make your test easier to write and understand.

Unit Test

You can take the advantage of new async/await usage of the MSTest method. This simplifies complicated and unreliable tests which we used to write.

A Unit Test that verify the correct redirect route controller/action methods, can be written as below.

    [TestMethod]
    public async Task Register_RegisterValidUser_EnsureRedirectToIndexActionHomeController()
    {
        // Arrange
        var userManagerStub = Substitute.For<IUserManager<ApplicationUser>>();
        var tcs = new TaskCompletionSource<IdentityResult>();
        tcs.SetResult(new IdentityResult(true));
        userManagerStub.CreateAsync(Arg.Any<ApplicationUser>(), Arg.Any<string>()).Returns(tcs.Task);

        var signInManagerStub = Substitute.For<ISignInManager>>();

        signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));

        var sut = new AccountController(userManagerStub) { SignInManager = signInManagerStub.Object };

        // Act
        var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;

        // Assert
        Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
        Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
    }

The above uses NSubstitute as the isolation framework, but if anyone interested in the Moq version, please see below.

    [TestMethod]
    public async Task Register_RegisterValidUser_EnsureRedirectToIndexHome()
    {
        // Arrange
        var userManagerStub = new Mock<IUserManager<ApplicationUser>>();
        var tcs = new TaskCompletionSource<IdentityResult>();
        tcs.SetResult(new IdentityResult(true));
        userManagerStub.Setup(s => s.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>())).Returns(tcs.Task);

        var signInManagerStub = new Mock<ISignInManager>();
        signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));

        var sut = new AccountController(userManagerStub.Object) {SignInManager = signInManagerStub.Object};

        // Act
        var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;

        // Assert
        Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
        Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top