12

次のように使用される UrlHelper 拡張メソッドのテストを作成しようとしています。

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>

ルートが登録されて機能していることを確認し、偽の HttpContext を作成するために Hanselmans 拡張機能を使用しています。私の UrlHelper extensionmethod は次のようになります。

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

それを機能させるために何が欠けているかについてのアイデアはありますか??? /クリストファー

4

3 に答える 3

11

それが機能しない理由は、RouteCollectionオブジェクトが内部的にHttpResponseBaseのApplyAppPathModifierメソッドを呼び出すためです。Hanselmanのモックコードはそのメソッドに期待を設定していないため、nullを返します。そのため、UrlHelperのActionメソッドへのすべての呼び出しは空の文字列を返します。修正は、HttpResponseBaseモックのApplyAppPathModifierメソッドに期待値を設定して、渡された値を返すだけにすることです。私はRhinoMocksの専門家ではないので、構文について完全にはわかりません。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; }
    }
}

上記のコードは、UrlHelperの単体テストに合格するために必要な最小限のHttpContextBaseの実装である必要があります。私はそれを試してみました、そしてそれはうまくいきました。お役に立てれば。

于 2010-06-20T05:55:23.060 に答える
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 を返しました。

于 2010-06-09T19:20:10.870 に答える
1

これがあなたの質問に直接答えないことはわかっていますが、MVC Futures アセンブリで利用可能なものを使用するのではなく、独自の汎用拡張メソッドを作成しようとしている理由はありますか? (Microsoft.Web.Mvc.dll) それとも、実際に msft の拡張メソッドを単体テストしようとしているのですか?

[編集 1] 申し訳ありませんが、Futures の Html ヘルパー拡張について考えていました。

それまでの間、単体テストを試して、同じ結果が得られるかどうかを確認します。

[編集 2] わかりました、これはまだ完全には機能していませんが、爆発していません。結果は、空の文字列を返すだけです。このリンクで Scott Hanselman からいくつかの Mvc モック ヘルパーを取得しました。

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

出力が空の文字列になる理由はまだわかりませんが、時間があるのでこれをいじり続けます。それまでの間、解決策が見つかったかどうかを知りたいと思います。

于 2009-06-04T15:57:26.573 に答える