0

我正在尝试对自定义操作结果进行单元测试。我最近观看了 Jimmy Bogard 的出色 MvcConf 视频(“让你的控制器节食”)http://www.viddler.com/explore/mvcconf/videos/1/并开始尝试实现一些自定义操作结果。我已经成功地做到了这一点,ActionResult 在运行时工作正常,但我在尝试对它们进行单元测试时遇到了麻烦。

不幸的是,在代码下载中没有针对 Jimmy 的自定义操作方法的单元测试......这让我感到奇怪。

我意识到动作方法只返回 ActionResult 类型的实例及其实际调用 ExecuteResult 方法的 MVC 框架,这在运行单元测试时当然不可用。所以我的单元测试现在只是创建我的自定义 ActionResult 的一个实例,然后我调用 ExecuteResult。

不幸的是,在我的自定义 ActionResult 的 ExecuteResult 方法中,它还调用了我传递给它的 ViewResult 的 ExecuteResult 方法。到时候就炸了。我应该如何模拟/存根这些东西以使我的单元测试正常工作?

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 中添加了评论以显示问题是否发生。

我知道我应该以某种方式存根/嘲笑,但我似乎无法解决这个问题。

4

1 回答 1

0

来自ASP.NET MVC 2 In Action(由 Jimmy Bogard 合着):

通过将难以测试的代码从操作中取出并将其放入操作结果的 Execute 方法中,您可以确保操作变得更容易进行单元测试。这是因为当你对一个动作进行单元测试时,你断言了动作返回的动作结果的类型和动作结果的状态。操作结果的 Execute 方法不作为单元测试的一部分执行。

单元测试旨在隔离行为和关注点。您通过在自定义操作中调用 ExecuteResult 来混合关注点。相反,我将SendToAFriendActionResult返回实际的 ActionResult(失败或成功):

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 之外。你的测试应该断言正确类型的动作,失败或成功,是根据你设置的参数返回的。

于 2010-08-06T16:59:45.683 回答