2009-06-04 16 views
12

Próbuję napisać test dla extensionmethod UrlHelper który jest używany tak:test jednostkowy Url.Action (przy użyciu Rhino Mocks?)

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

Ja jednak nie potrafię go skonfigurować tak poprawnie że mogę utworzyć nowy UrlHelper, a następnie potwierdzić, że zwrócony URL był oczekiwany. To jest to, co mam, ale jestem otwarty na wszystko, co nie wiąże się również ze szyderstwem. ; 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")); 
    } 

Próbuję zmienić go urlHelper.Action („Test”, „TestAction”), ale to nie tak więc wiem, że to nie jest mój extensionmethod że nie działa. NUnit powraca:

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

I sprawdzeniu, że trasa jest zarejestrowana i działa i używam rozszerzenia Hanselmans do tworzenia fałszywego HTTPContext. Oto, jak wygląda metoda przedłużania mojego UrlHelper:

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

Jakieś pomysły na temat tego, czego mi brakuje, aby to działało ??? /Kristoffer

+0

Czy można wysłać kod metody Factory.CreateUrlHelper? – nkirkes

Odpowiedz

11

Powodem, dla którego nie działa, jest wewnętrznie obiekt RouteCollection wywołuje metodę ApplyAppPathModifier w HttpResponseBase.Wygląda na to, że sztuczny kod Hanselmana nie stawia żadnych oczekiwań dla tej metody, więc zwraca wartość null, dlatego wszystkie twoje wywołania metody Action na UrlHelper zwracają pusty ciąg. Rozwiązaniem byłoby ustawienie oczekiwań na metodę ApplyAppPathModifier w procedurze HttpResponseBase, aby po prostu zwrócić wartość, która jest do niej przekazywana. Nie jestem ekspertem od Rhino Mocks, więc nie jestem całkowicie pewien składni. Jeśli używasz Min, wtedy to będzie wyglądać następująco:

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

Lub jeśli po prostu użyć ręcznie walcowane makiety, coś jak to będzie działać:

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

Powyższy kod powinien być minimalna niezbędna implementacja HttpContextBase w celu przejścia testu jednostkowego dla UrlHelper. Wypróbowałem to i zadziałało. Mam nadzieję że to pomoże.

1

Wiem, że to nie odpowiada bezpośrednio na twoje pytanie, ale czy istnieje powód, dla którego próbujesz napisać własną ogólną metodę rozszerzenia w przeciwieństwie do tej, która jest dostępna w zestawie MVC Futures? (Microsoft.Web.Mvc.dll) Czy faktycznie próbujesz testować metodę rozszerzenia msft?

[Edytuj 1] Niestety, myślałem o rozszerzeniu helpera Html w Futures.

W międzyczasie, spróbuję swoich sił w teście jednostek, aby sprawdzić, czy otrzymam taki sam wynik.

[Edytuj 2] OK, więc to jeszcze nie działa, ale nie wysadza w powietrze. Wynikiem jest po prostu zwracanie pustego ciągu znaków. Wziąłem trochę MVC szyderczy pomocników Scott Hanselman na this link.

ja też stworzył metodę Url.Action<TController>, wraz z metod pomocniczych I zgrane ze źródła 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); 
     } 
    } 
} 

i wreszcie, oto test biegnę :

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

Nie wiem jeszcze dlaczego wyjście jest pusty łańcuch, ale będę ingerować w to jak mam czas. Byłbym ciekawy, czy znajdziesz rozwiązanie w międzyczasie.

+0

Zaktualizowałem swoje przykłady, a teraz otrzymuję taki sam wynik jak ty i pewnie można go nazwać postępem. Nadal nie wiem, dlaczego otrzymuję pusty ciąg z powrotem, ponieważ sprawdziłem, że moja trasa działa i pasuje do "~/Test/TestAction". –

+0

Tak, nie jestem pewien, dlaczego to też wróci. Na początku myślałem, że to dlatego, że w projekcie przeprowadziłem ten test, mam kilka dziwnych tras i być może nie udało mi się znaleźć dopasowania. Ponieważ uzyskujesz taki sam wynik, nie jestem pewien, czy jest to problem z routingiem. Zagrałem jeszcze trochę. – nkirkes

2

udało mi się przetestować metodę BuildUrlFromExpression, ale musiałem zarejestrować moje RouteTable.Routes przed uruchomieniem testów:

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

Następnie skrótową out/setup te właściwości:

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

Po że metoda BuildUrlFromExpression zwróciła uls zgodnie z oczekiwaniami.

Powiązane problemy