8

I am quite new to Unit testing and Mock concepts. I am trying to figure out how to write a good test case for the basic out-of-the box user registration code below:

[HttpPost]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        MembershipCreateStatus createStatus;
        Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);

        if (createStatus == MembershipCreateStatus.Success)
        {
            FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError("", ErrorCodeToString(createStatus));
        }
    }

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

Below are some of the specific points where I need your opinion/help:

  1. I do not necessarily want to create a new user in ASP .Net membership database.
  2. Based on the model passed in, how do I really make sure that if the user was successfully registered or there were errors in the process.
halfer
  • 19,824
  • 17
  • 99
  • 186
Moon
  • 33,439
  • 20
  • 81
  • 132

2 Answers2

25

You have a problem with your code. Your action depends on a static method: Membership.CreateUser. And as you know static methods are PITAs to unit test.

So you could weaken the coupling by introducing a level of abstraction:

public interface IMyService
{
    MembershipCreateStatus CreateUser(string username, string password, string email);
}

and then have some implementation that would use the current Membership provider:

public class MyService: IMyService
{
    public MembershipCreateStatus CreateUser(string username, string password, string email)
    {
        MembershipCreateStatus status;
            Membership.CreateUser(username, password, email, null, null, true, null, out status);
        return status;
    }
}

and finally the controller:

public class AccountController : Controller
{
    private readonly IMyService _service;
    public AccountController(IMyService service)
    {
        _service = service;
    }

    [HttpPost]
    public ActionResult Register(RegisterModel model)
    {
        if (ModelState.IsValid)
        {
            // Attempt to register the user
            var status = _service.CreateUser(model.UserName, model.Password, model.Email);
            if (status == MembershipCreateStatus.Success)
            {
                FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                ModelState.AddModelError("", ErrorCodeToString(createStatus));
            }
        }

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

OK, now that we have weakened the coupling we could use a mocking framework to mock the service in the unit test and make it trivial.

For example using Rhino Mocks you could create the following tests to cover the 2 failure cases:

[TestMethod]
public void Register_Action_Should_Redisplay_View_If_Model_Is_Invalid()    
{
    // arrange
    var sut = new AccountController(null);
    var model = new RegisterModel();
    sut.ModelState.AddModelError("", "invalid email");

    // act
    var actual = sut.Register(model);

    // assert
    Assert.IsInstanceOfType(actual, typeof(ViewResult));
    var viewResult = actual as ViewResult;
    Assert.AreEqual(model, viewResult.Model);
}

[TestMethod]
public void Register_Action_Should_Redisplay_View_And_Add_Model_Error_If_Creation_Fails()
{
    // arrange
    var service = MockRepository.GenerateStub<IMyService>();
    service
        .Stub(x => x.CreateUser(null, null, null))
        .IgnoreArguments()
        .Return(MembershipCreateStatus.InvalidEmail);
    var sut = new AccountController(service);
    var model = new RegisterModel();

    // act
    var actual = sut.Register(model);

    // assert
    Assert.IsInstanceOfType(actual, typeof(ViewResult));
    var viewResult = actual as ViewResult;
    Assert.AreEqual(model, viewResult.Model);
    Assert.IsFalse(sut.ModelState.IsValid);
}

The final test is the success case. We still have an issue with it. The issue is the following line:

FormsAuthentication.SetAuthCookie(model.UserName, false);

What is this? It is a static method call. So we proceed the same way as we did with the membership provider to weaken the coupling of our controller and the forms authentication system.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
0

To test this method you can follow two way

  1. Inside the test class create a new class that inherits from Membership class and override the method CreateUser.
  2. Use Moq to mock the class.

For the first case I will check if username is equal to "GoodUser" or "BadUser" and generate a MembershipCreateStatus.Success or a different status.

For the second I will setup two method that follow the same idea as in the other method. See this link for an example

Community
  • 1
  • 1
Iridio
  • 9,213
  • 4
  • 49
  • 71