Подтвердить что ты не робот

Unittesting Url.Action(используя Rhino Mocks?)

Я пытаюсь написать тест для метода расширения UrlHelper, который используется следующим образом:

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

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

        [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:

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

Любые идеи о том, что мне не хватает, чтобы заставить его работать??? /Кристоффер

4b9b3361

Ответ 1

Причина, по которой он не работает, заключается в том, что внутри объекта 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, чтобы сделать проход unit test для UrlHelper. Я попробовал, и это сработало. Надеюсь, это поможет.

Ответ 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, как ожидалось.

Ответ 3

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

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

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

[Редактировать 2] Хорошо, так что это еще не все работает, но это не взрывается. Результат просто возвращает пустую строку. Я взял некоторые Mkc насмешливые помощники Скотта Гензельмана в эту ссылку.

Я также создал метод 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);
    }

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