Как выполнить юнит-тест собственного результата действия - PullRequest
0 голосов
/ 06 августа 2010

Я пытаюсь провести модульное тестирование результата пользовательского действия. Недавно я смотрел превосходное видео Джимми Богарда на MvcConf («положи свои контроллеры на диету») http://www.viddler.com/explore/mvcconf/videos/1/ и начал пытаться реализовать некоторые результаты специальных действий. Я справился с этим без проблем, ActionResult прекрасно работает во время выполнения, но у меня возникают проблемы при попытке их модульного тестирования.

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

Я понимаю, что методы действия просто возвращают экземпляры типов ActionResult и его среду MVC, которая фактически вызывает метод ExecuteResult, который, конечно, недоступен при запуске модульного теста. Поэтому мой модульный тест сейчас просто создает экземпляр моего пользовательского ActionResult, и я затем вызываю ExecuteResult.

К сожалению, в методе ExecuteResult моего пользовательского ActionResult он также вызывает метод ExecuteResult объекта ViewResult, который я ему передал. В этот момент он взрывается. Как я должен насмехаться над этими вещами, чтобы мой модульный тест работал?

public class SendToAFriendActionResult : ActionResult
{

    public const string INVALID_CAPTCHA = "You don't appear to have filled out the two words from the security image correctly to prove you're a human. Please try again.";
    public const string INVALID_MODEL_STATE = "You don't appear to have filled out all the details correctly. Please try again.";
    public const string CONTACT_FAIL = "Unfortunately we experiend a problem sending the link. Please try again later.";
    public const string SEND_TO_A_FRIEND_FAIL_KEY = "ContactFail";

    private  RedirectResult _success;
    private  ViewResult _failure;
    private readonly SendToAFriendModel _model;
    private readonly bool _captchaValid;
    private readonly MessageBuilderServiceBase _mbs;

    public RedirectResult Success
    {
        get { return _success; }
        set { _success = value; }
    }

    public ViewResult Failure
    {
        get { return _failure; }
        set { _failure = value; }
    }

    public SendToAFriendActionResult(RedirectResult success, ViewResult failure, SendToAFriendModel model, bool captchaValid, MessageBuilderServiceBase mbs)
    {
        _success = success;
        _failure = failure;
        _model = model;
        _captchaValid = captchaValid;
        _mbs = mbs;
    }

    public override void ExecuteResult(ControllerContext context)
    {

        if (!_captchaValid)
        {
            Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_CAPTCHA;

            // On reaching this point I receive the error
            // Object reference not set to an instance of an object
            // as the MVC framework calls FindView 
            Failure.ExecuteResult(context);
            return;
        }

        if (!context.Controller.ViewData.ModelState.IsValid)
        {
            Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_MODEL_STATE;
            Failure.ExecuteResult(context);
            return;
        }

        _mbs.RecipientEmailAddress = _model.EmailRecipient;
        _mbs.SendersName = _model.SendersName;
        _mbs.Url = _model.URL;
        var result = _mbs.sendMessage();

        if (!result)
        {
            Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = CONTACT_FAIL;
            Failure.ExecuteResult(context);
            return;
        }

        Success.ExecuteResult(context);
    }
}

Вот начало моего юнит-теста ...

        IMessageService _emailMessageSerivce;
        IGalleryRepository _repository;

        var stfModel = new SendToAFriendModel
        {
            SendersName = "Someone",
            URL = "http://someurl.com",
            EmailRecipient = "a-friend@somewherelse.com"
        };

        var failure = new ViewResult() {ViewName ="SendToFriend"};
        const bool captchaValid = false;
        var fakeControlllerContext = MockRepository.GenerateStub<ControllerContext>(null);

        var stf = new SendToAFriendActionResult(null, failure, stfModel, captchaValid, null);
        stf.ExecuteResult(fakeControlllerContext);

Я поместил комментарии в SUT, чтобы показать, где возникает проблема.

Я знаю, что должен как-то ошарашить / издеваться, но я просто не могу решить эту проблему.

1 Ответ

0 голосов
/ 06 августа 2010

С ASP.NET MVC 2 в действии (в соавторстве с Джимми Богардом):

Сняв этот сложный для тестирования код действия и положить его в Выполнить метод результата действия, Вы гарантируете, что действия становятся значительно проще для юнит-теста. Это потому, что когда вы тестируете действие, вы утверждаете тип действия результат, что действие возвращается и состояние результата действия. Выполнить метод результата действия не выполняется как часть подразделения тест.

Модульные тесты предназначены для изоляции поведения и проблем. Вы смешиваете проблемы, вызывая ExecuteResult из своего собственного действия. Вместо этого я бы SendToAFriendActionResult вернул фактический ActionResult (Failure или Success):

public ActionResult GetAction(..)
{
   ActionResult result;
   //logic here to determine which ActionResult to return
   return result;
}

В вашем контроллере:

  public ViewResult SendToAFriend()
    {
       return SendToAFriendActionResult(null, failure, stfModel, captchaValid, null)
            .GetAction();
    }

Этот метод позволит инфраструктуре MVC выполнять свою работу и изолировать эти проблемы за пределами вашего пользовательского ActionResult. Ваш тест должен утверждать, что правильный тип действия, неудачи или успеха возвращается на основе заданных вами параметров.

...