2009-06-04 3 views
12

Я пытаюсь написать тест на качестве метод расширения UrlHelper, который используется, как это:модульное тестирование Url.Action (с помощью Rhino Mocks?)

Url.Action<TestController>(x => x.TestAction()); 

Однако, я не могу установить его правильно так что я могу создать новый UrlHelper, а затем утверждать, что возвращаемый url был ожидаемым. Это то, что у меня есть, но я открыт для всего, что не включает насмешливость. ; O)

 [Test] 
    public void Should_return_Test_slash_TestAction() 
    { 
     // Arrange 
     RouteTable.Routes.Add("TestRoute", new Route("{controller}/{action}", new MvcRouteHandler())); 
     var mocks = new MockRepository(); 
     var context = mocks.FakeHttpContext(); // the extension from hanselman 
     var helper = new UrlHelper(new RequestContext(context, new RouteData()), RouteTable.Routes); 

     // Act 
     var result = helper.Action<TestController>(x => x.TestAction()); 

     // Assert 
     Assert.That(result, Is.EqualTo("Test/TestAction")); 
    } 

Я попытался изменить его urlHelper.Action («Test», «TestAction»), но она не будет выполнена в любом случае, так что я знаю, что это не мой метод расширения, который не работает. NUnit возвращается:

NUnit.Framework.AssertionException: Expected string length 15 but was 0. Strings differ at index 0. 
Expected: "Test/TestAction" 
But was: <string.Empty> 

Я проверил, что маршрут зарегистрирован и работает, и я использую расширение Hanselmans для создания поддельной HttpContext. Вот что мой UrlHelper extentionmethod выглядеть следующим образом:

 public static string Action<TController>(this UrlHelper urlHelper, Expression<Func<TController, object>> actionExpression) where TController : Controller 
    { 
     var controllerName = typeof(TController).GetControllerName(); 
     var actionName = actionExpression.GetActionName(); 

     return urlHelper.Action(actionName, controllerName); 
    } 

    public static string GetControllerName(this Type controllerType) 
    { 
     return controllerType.Name.Replace("Controller", string.Empty); 
    } 

    public static string GetActionName(this LambdaExpression actionExpression) 
    { 
     return ((MethodCallExpression)actionExpression.Body).Method.Name; 
    } 

Любые идеи о том, что мне не хватает, чтобы это заработало ??? /Kristoffer

+0

Можете ли вы опубликовать свой код метода Factory.CreateUrlHelper? – nkirkes

ответ

11

Причина, по которой она не работает, заключается в том, что внутри объекта RouteCollection вызывает метод ApplyAppPathModifier на HttpResponseBase.Похоже, что фальшивый код Hanselman не дает никаких ожиданий в отношении этого метода, поэтому он возвращает null, поэтому все ваши вызовы метода Action на UrlHelper возвращают пустую строку. Исправлено бы установить математическое ожидание метода ApplyAppPathModifier макета HttpResponseBase, чтобы просто вернуть значение, которое передается в него. Я не эксперт Rhino Mocks, поэтому я не совсем уверен в синтаксисе. Если вы используете Moq, то это будет выглядеть следующим образом:

httpResponse.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())) 
    .Returns((string s) => s); 

Или, если вы просто использовать ручную прокатку издеваются, что-то, как это будет работать:

internal class FakeHttpContext : HttpContextBase 
{ 
    private HttpRequestBase _request; 
    private HttpResponseBase _response; 

    public FakeHttpContext() 
    { 
     _request = new FakeHttpRequest(); 
     _response = new FakeHttpResponse(); 
    } 

    public override HttpRequestBase Request 
    { 
     get { return _request; } 
    } 

    public override HttpResponseBase Response 
    { 
     get { return _response; } 
    } 
} 

internal class FakeHttpResponse : HttpResponseBase 
{ 
    public override string ApplyAppPathModifier(string virtualPath) 
    { 
     return virtualPath; 
    } 
} 

internal class FakeHttpRequest : HttpRequestBase 
{ 
    private NameValueCollection _serverVariables = new NameValueCollection(); 

    public override string ApplicationPath 
    { 
     get { return "/"; } 
    } 

    public override NameValueCollection ServerVariables 
    { 
     get { return _serverVariables; } 
    } 
} 

Приведенные выше код должен быть минимальная необходимая реализация HttpContextBase, чтобы сделать единичный тест для UrlHelper. Я попробовал, и это сработало. Надеюсь это поможет.

1

Я знаю, что это напрямую не отвечает на ваш вопрос, но есть ли причина, по которой вы пытаетесь написать свой собственный общий метод расширения, а не использовать тот, который доступен в сборке MVC Futures? (Microsoft.Web.Mvc.dll) Или вы на самом деле пытаетесь установить метод расширения msft?

[Изменить 1] Извините, я думал о расширении Html-помощника в Futures.

В то же время, я попробую свои силы при единичном тестировании, чтобы узнать, получил ли я тот же результат.

[Редактировать 2] Хорошо, так что это еще не все работает, но это не взрывается. Результат просто возвращает пустую строку. Я взял некоторые Mvc издевательский хелперы от Скотта Hanselman в this link.

Я также создал метод Url.Action<TController>, наряду с вспомогательными методами, скопированные из источника Mvc:

public static string Action<TController>(this UrlHelper helper, Expression<Action<TController>> action) where TController : Controller 
{ 
    string result = BuildUrlFromExpression<TController>(helper.RequestContext, helper.RouteCollection, action); 
    return result; 
} 

public static string BuildUrlFromExpression<TController>(RequestContext context, RouteCollection routeCollection, Expression<Action<TController>> action) where TController : Controller 
{ 
    RouteValueDictionary routeValuesFromExpression = GetRouteValuesFromExpression<TController>(action); 
    VirtualPathData virtualPath = routeCollection.GetVirtualPath(context, routeValuesFromExpression); 
    if (virtualPath != null) 
    { 
     return virtualPath.VirtualPath; 
    } 
    return null; 
} 

public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) where TController : Controller 
{ 
    if (action == null) 
    { 
     throw new ArgumentNullException("action"); 
    } 
    MethodCallExpression body = action.Body as MethodCallExpression; 
    if (body == null) 
    { 
     throw new ArgumentException("MvcResources.ExpressionHelper_MustBeMethodCall", "action"); 
    } 
    string name = typeof(TController).Name; 
    if (!name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) 
    { 
     throw new ArgumentException("MvcResources.ExpressionHelper_TargetMustEndInController", "action"); 
    } 
    name = name.Substring(0, name.Length - "Controller".Length); 
    if (name.Length == 0) 
    { 
     throw new ArgumentException("MvcResources.ExpressionHelper_CannotRouteToController", "action"); 
    } 
    RouteValueDictionary rvd = new RouteValueDictionary(); 
    rvd.Add("Controller", name); 
    rvd.Add("Action", body.Method.Name); 
    AddParameterValuesFromExpressionToDictionary(rvd, body); 
    return rvd; 
} 

private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call) 
{ 
    ParameterInfo[] parameters = call.Method.GetParameters(); 
    if (parameters.Length > 0) 
    { 
     for (int i = 0; i < parameters.Length; i++) 
     { 
      Expression expression = call.Arguments[i]; 
      object obj2 = null; 
      ConstantExpression expression2 = expression as ConstantExpression; 
      if (expression2 != null) 
      { 
       obj2 = expression2.Value; 
      } 
      else 
      { 
       Expression<Func<object>> expression3 = Expression.Lambda<Func<object>>(Expression.Convert(expression, typeof(object)), new ParameterExpression[0]); 
       obj2 = expression3.Compile()(); 
      } 
      rvd.Add(parameters[i].Name, obj2); 
     } 
    } 
} 

И, наконец, вот тест я бегу :

[Test] 
    public void GenericActionLinkHelperTest() 
    { 
     RouteRegistrar.RegisterRoutesTo(RouteTable.Routes); 

     var mocks = new MockRepository(); 
     var context = mocks.FakeHttpContext(); // the extension from hanselman 

     var helper = new UrlHelper(new RequestContext(context, new RouteData()), RouteTable.Routes); 
     string result = helper.Action<ProjectsController>(x => x.Index()); 

     // currently outputs an empty string, so something is fudded up. 
     Console.WriteLine(result); 
    } 

Не уверен, почему вывод пустой строки, но я буду продолжать возиться с этим, поскольку у меня есть время. Мне было бы интересно узнать, находите ли вы решение тем временем.

+0

Я обновил свои примеры, и теперь я получаю тот же результат, что и вы, и я думаю, вы могли бы назвать этот прогресс. Все еще не уверен, почему я возвращаю пустую строку, поскольку я проверял, что мой маршрут работает, и соответствует «~/Test/TestAction». –

+0

Да, я не уверен, почему это тоже возвращается. Сначала я подумал, что это связано с тем, что в проекте я провел этот тест, у меня есть нечетные маршруты и, возможно, он не смог найти совпадение. Поскольку вы получаете тот же результат, я не уверен, что это проблема маршрутизации. Я поиграю еще немного. – nkirkes

2

Я был в состоянии проверить метод BuildUrlFromExpression, но мне нужно, чтобы зарегистрировать свои RouteTable.Routes перед запуском тестов:

[ClassInitialize] 
public static void FixtureSetUp(TestContext @__testContext) 
{ 
    MvcApplication.RegisterRoutes(RouteTable.Routes); 
} 

Затем гашу/настройку этих свойств:

HttpRequestBase request = mocks.PartialMock<HttpRequestBase>(); 
request.Stub(r => r.ApplicationPath).Return(string.Empty); 

HttpResponseBase response = mocks.PartialMock<HttpResponseBase>(); 
SetupResult.For(response.ApplyAppPathModifier(Arg<String>.Is.Anything)).IgnoreArguments().Do((Func<string, string>)((arg) => { return arg; })); 

После что метод BuildUrlFromExpression вернул uls, как ожидалось.

Смежные вопросы