8

我用来ControllerActionInvoker从单元测试中调用控制器操作

var controllerInvoker = new ControllerActionInvoker();
var result = controllerInvoker.InvokeAction(
                 testController.ControllerContext, "Default" );

如何使用它来调用具有参数的操作?

[AcceptVerbs( HttpVerbs.Post )]
[ActionException( SomeAttribute )]
public SomeResult AddMethod( long[] Ids )
{
    //some code
}
4

3 回答 3

1

从文档看来,您想使用 InvokeActionMethod 方法,该方法允许您将 IDictionary 中的参数作为第三个参数传递。

ControllerContext 实际上带有控制器将用于绑定的附加数据(过滤器、模型绑定器、路由数据)。您的参数将需要通过 ControllerContext 传递。

我找到了一个关于单元测试控制器的例子

于 2009-07-17T19:47:44.557 回答
0

您不应该在单元测试中使用 ControllerActionInvoker。你实际上想要完成什么?

如果您尝试测试您的操作的行为,只需直接调用它们(它们只是常规方法)。如果您尝试测试过滤器的行为,请为过滤器创建一个模拟上下文并调用其 OnXxx() 方法。

于 2009-07-20T17:47:52.007 回答
0

我使用 ControllerActionInvoker 是因为我想围绕我的控制器编写规范测试,而不是低级单元测试。我发现我对 ControllerActionInvoker 的实现必须根据我正在测试的内容进行改进,但以下内容对我有用。

class ControllerSpecActionInvoker<TResult> : ControllerActionInvoker where 
TResult : ActionResult
{
  private readonly Expression body;

  public ControllerSpecActionInvoker(Expression body)
  {
    this.body = body;
  }

  public TResult Result { get; private set; }

  protected override void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
    => Result = actionResult as TResult;

  protected override IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
  {
    if (body is MethodCallExpression methodCall)
    {
      return methodCall.Method.GetParameters()
        .Zip(methodCall.Arguments.Select(GetArgumentAsConstant), (param, arg) => new { param.Name, Value = ChangeType(arg.Value, param.ParameterType) })
          .ToDictionary(item => item.Name, item => item.Value);
    }
    return base.GetParameterValues(controllerContext, actionDescriptor);
  }

  private ConstantExpression GetArgumentAsConstant(Expression exp)
  {
    switch (exp)
    {
      case ConstantExpression constExp:
        return constExp;
      case UnaryExpression uranExp:
        return GetArgumentAsConstant(uranExp.Operand);
    }

    throw new NotSupportedException($"Cannot handle expression of type '{exp.GetType()}'");
  }

  private static object ChangeType(object value, Type conversion)
  {
    var t = conversion;

    if (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(Nullable<>)) return Convert.ChangeType(value, t);

    if (value == null) return null;

    t = Nullable.GetUnderlyingType(t);

    return Convert.ChangeType(value, t);
  }
}

出于我的目的,这用于规范夹具基类和自动模拟依赖项,但您使用它的本质是这样的:

var actionInvoker = new ControllerSpecActionInvoker<ActionResult>(Expression<Func<ActionResult|JsonResult|Etc>>);
actionInvoker.InvokeAction(<controller context>, <name of the action>);
Result = actionInvoker.Result;

所以它可能看起来像这样,不是测试。大多数杂乱无章的东西可以隐藏在基类中:

class MyController : Controller
{
  JsonResult MyAction(int i) { return Json(new {}); } 
}

class MyControllerFixture
{
  [Test]
  public void ReturnsData()
  {
    var controller = new MyController();
    var controllerContext = new ControllerContext
    {
      RouteData = new RouteData(),
      HttpContext = httpContextBase,
    };
    controllerContext.Controller = controller;
    controller.ControllerContext = controllerContext;
    Action<JsonResult> act = controller.MyAction(1);
    var actionInvoker = new ControllerSpecActionInvoker<JsonResult>(act.Body);
    actionInvoiker.Result.Should().NotBeNull();
  }
}
于 2018-06-27T22:07:56.843 回答