我正在尝试为这样使用的UrlHelper扩展方法编写测试:
Url.Action<TestController>(x => x.TestAction());
但是,我似乎无法正确设置它,以便我可以创建一个新的UrlHelper,然后断言返回的url是预期的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 :(得分:11)
它无法工作的原因是RouteCollection对象在内部调用HttpResponseBase上的ApplyAppPathModifier方法。看起来Hanselman的模拟代码没有对该方法设置任何期望,因此它返回null,这就是为什么你对UrlHelper上的Action方法的所有调用都返回一个空字符串。修复是在HttpResponseBase mock的ApplyAppPathModifier方法上设置一个期望,只返回传递给它的值。我不是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 :(得分: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。
答案 2 :(得分:1)
我知道这并没有直接回答你的问题,但是你是否有理由尝试编写自己的通用扩展方法而不是使用MVC Futures程序集中提供的方法? (Microsoft.Web.Mvc.dll)或者您是否正在尝试对msft的扩展方法进行单元测试?
[编辑1] 对不起,我在考虑期货中的Html助手扩展。
与此同时,我会尝试单位测试,看看是否得到相同的结果。
[编辑2] 好的,所以这还没有完全发挥作用,但它并没有爆发。结果只是返回一个空字符串。我在this link.
带了一些来自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);
}
还不确定为什么输出是一个空字符串,但我会一直搞乱这个,因为我有时间。我很想知道你是否在此期间找到了解决方案。