2010-10-01 2 views
6

Я сделал поиск по SO и, похоже, этот вопрос задается довольно часто. Я был в состоянии заставить mocks работать, и я также могу выполнить OnActionExecuted() без каких-либо проблем. Вот мой модульный тест. Прокомментированные строки - это те, которые терпят неудачу, и я уверен, что я не издеваюсь над правильным типом.Unit Testing Action Filters - Как насмехаться ViewResult

 //Arrange 
     //var viewResult = new ViewResult(); 
     var filterContextMock = new Mock<ActionExecutedContext>(); 
     var routeData = new RouteData(); 
     var httpContextMock = new Mock<HttpContextBase>(); 

     routeData.Values["data"] = "Mock data"; 
     var requestContext = new RequestContext(httpContextMock.Object, routeData); 

     var controller = new FakeController(); 
     controller.ControllerContext = new ControllerContext(requestContext, controller); 

     filterContextMock.Setup(f => f.RouteData).Returns(routeData); 
     filterContextMock.Setup(f => f.Controller).Returns(controller); 
     //filterContextMock.Setup(f => f.Result).Returns(viewResult); 

     //Act 
     var wrapFilterAttribute = new WrapFilterAttribute(); 
     wrapFilterAttribute.OnActionExecuted(filterContextMock.Object); 

Вот мой фильтр действий.

public class WrapFilterAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     var view = (ViewResultBase)filterContext.Result; 

     if (view != null) 
     { 
      BaseViewModel viewModel = (BaseViewModel)view.ViewData.Model ?? new BaseViewModel(); 
      viewModel.Wrap = new WrapperFactory().GetWrap(); 
     } 

     base.OnActionExecuted(filterContext); 
    } 
} 

Проблема, с которой я столкнулся, это filterContext.Result всегда приходит как EmptyResult. Вместо этого я хотел бы использовать гидратированный ViewResult. Любые идеи, как я могу это сделать?

Большое спасибо!

ответ

9

Во-первых, давайте начнем фиксируя свой фильтр действия в настоящее время код выглядит плохо, и эти отливки может принести головную боль:

public class WrapFilterAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     var view = filterContext.Result as ViewResultBase; 
     if (view != null) 
     { 
      // the controller action returned a ViewResultBase 
      var viewModel = view.ViewData.Model as BaseViewModel; 
      if (viewModel != null) 
      { 
       // the model passed to the view was derived from 
       // BaseViewModel so we can safely update the Wrap 
       // property 
       viewModel.Wrap = new WrapperFactory().GetWrap(); 
      } 
     } 
     base.OnActionExecuted(filterContext); 
    } 
} 

И тестовое устройство:

// arrange 
var sut = new WrapFilterAttribute(); 
var filterContextMock = new Mock<ActionExecutedContext>(); 
var viewResultMock = new Mock<ViewResultBase>(); 
filterContextMock.Object.Result = viewResultMock.Object; 
var viewModel = new BaseViewModel(); 
viewResultMock.Object.ViewData.Model = viewModel; 

// act 
sut.OnActionExecuted(filterContextMock.Object); 

// assert 
// TODO: assert something on the viewModel.Wrap property like 
// for example that it has been initialized 

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

+0

Спасибо за рефакторинг моего дрянного кода Дарин. Это сработало для меня. Еще раз спасибо :) – Praveen

+0

Ваш комментарий о введении WrapperFactory в конструктор фильтра действий вызывает у меня любопытство. Как бы вы выполнили отправку в экземпляр WrapperFactory через WrapFilterAttribute? AFAIK, атрибуты требуют значений времени компиляции (константы и т. Д.). – Praveen

+1

@Praveen, вот пример того, как достичь [инъекции конструктора с фильтрами действия] (http://iridescence.no/post/Constructor-Injection-for-ASPNET-MVC-Action-Filters.aspx). Это немного хаки, но это единственный способ на данный момент. Это было улучшено в ASP.NET MVC 3. –