Модульное тестирование с ControllerActionInvoker для вызова действия с параметрами - PullRequest
8 голосов
/ 17 июля 2009

Я использую ControllerActionInvoker для вызова действий контроллера из модульных тестов

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

Как я могу использовать его для вызова действия с параметрами?

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

Ответы [ 3 ]

1 голос
/ 17 июля 2009

Из документации видно, что вы хотите использовать метод InvokeActionMethod, который позволяет передавать параметры в IDictionary в качестве третьего аргумента.

ControllerContext фактически несет с собой дополнительные данные, которые контроллер будет использовать для привязки (фильтры, привязки модели, данные маршрута). Ваш аргумент должен быть передан через ControllerContext.

Я нашел пример о контроллерах модульного тестирования .

0 голосов
/ 28 июня 2018

Я использую 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();
  }
}
0 голосов
/ 20 июля 2009

Вы не должны использовать ControllerActionInvoker из своих модульных тестов. Чего вы на самом деле пытаетесь достичь?

Если вы пытаетесь проверить поведение ваших действий, просто вызывайте их напрямую (это просто обычные методы). Если вы пытаетесь проверить поведение ваших фильтров, создайте фиктивный контекст для фильтра и вызовите его метод OnXxx ().

...