ASP .Net MVC 3: модульное тестирование действий контроллера - PullRequest
8 голосов
/ 21 марта 2012

Я новичок в модульном тестировании и концепциях Mock.Я пытаюсь выяснить, как написать хороший тестовый пример для базового готового кода регистрации пользователя ниже:

[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);
}

Ниже приведены некоторые конкретные моменты, в которых мне нужно ваше мнение / помощь:

  1. Я не обязательно хочу создавать нового пользователя в базе данных членства ASP .Net.
  2. На основе переданной модели как мне на самом делеубедитесь, что, если пользователь был успешно зарегистрирован или в процессе были ошибки.

Ответы [ 2 ]

25 голосов
/ 21 марта 2012

У вас проблема с вашим кодом. Ваше действие зависит от статического метода: Membership.CreateUser. И, как вы знаете, статические методы являются PITA для модульного тестирования.

Чтобы вы могли ослабить связь, введя уровень абстракции:

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

, а затем есть некоторая реализация, которая будет использовать текущий поставщик членства:

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;
    }
}

и, наконец, контроллер:

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);
    }
}

Хорошо, теперь, когда мы ослабили связь, мы могли бы использовать фиктивную структуру, чтобы смоделировать сервис в модульном тесте и сделать его тривиальным.

Например, используя Rhino Mocks, вы можете создать следующие тесты, чтобы охватить 2 случая отказа:

[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);
}

Последний тест - это случай успеха. У нас все еще есть проблема с этим. Вопрос в следующей строке:

FormsAuthentication.SetAuthCookie(model.UserName, false);

Что это? Это статический вызов метода. Таким образом, мы поступаем так же, как и с провайдером членства, чтобы ослабить связь нашего контроллера и системы аутентификации форм.

0 голосов
/ 21 марта 2012

Для проверки этого метода вы можете следовать в двух направлениях

  1. Внутри тестового класса создайте новый класс, который наследуется от Membership класса, и переопределите метод CreateUser.
  2. Используйте Moq, чтобы издеваться над классом.

Для первого случая я проверю, равно ли имя пользователя «GoodUser» или «BadUser», и сгенерирую MembershipCreateStatus.Success или другой статус.

Для второго я настрою два метода, которые следуют той же идее, что и в другом методе. Смотрите ссылку для примера

...