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

Утверждение JsonResult, содержащего анонимный тип

Я пытался использовать unit test метод в одном из моих контроллеров, возвращающих JsonResult. К моему удивлению, следующий код не работал:

[HttpPost]
public JsonResult Test() {
    return Json(new {Id = 123});
}

Вот как я его тестирую (также обратите внимание, что тестовый код находится в другой сборке):

// Act
dynamic jsonResult = testController.Test().Data;

// Assert
Assert.AreEqual(123, jsonResult.Id);

Assert генерирует исключение:

'object' не содержит определения для 'Id'

С тех пор я разрешил это, используя следующее:

[HttpPost]
public JsonResult Test() {
   dynamic data = new ExpandoObject();
   data.Id = 123;
   return Json(data);
}

Я пытаюсь понять, почему не первый работает? Он также, похоже, работает с чем угодно, но анонимным типом.

4b9b3361

Ответ 1

Чтобы быть ясным, конкретная проблема, с которой вы сталкиваетесь, заключается в том, что динамика С# не работает с непубличными членами. Это по дизайну, по-видимому, препятствовать тому, что происходит. Поскольку, как сказал LukLed, анонимные типы являются общедоступными только в пределах одной сборки (точнее, анонимные типы просто отмечены internal, а не public)), вы сталкиваетесь с этим барьером.

Возможно, самым чистым решением будет использование InternalsVisibleTo. Это позволяет вам называть другую сборку, которая может получить доступ к своим непубличным членам. Использование его для тестов является одной из основных причин его существования. В вашем примере вы разместите в своем основном проекте AssemblyInfo.cs следующую строку:

[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")]

Как только вы это сделаете, ошибка исчезнет (я просто попробовал это сам).

В качестве альтернативы вы могли бы использовать только выражение грубой силы:

Assert.AreEqual(123, jsonResult.GetType().GetProperty("Id").GetValue(jsonResult, null));

Ответ 2

Прочитав ответы здесь, а затем посмотрев дальше, я нашел сообщение 2009 msdn с другим подходом. Но.. в комментариях было очень простое и очень элегантное решение Kieran... использовать .ToString().

В вашем исходном случае:

[HttpPost]
public JsonResult Test()
{
    return Json(new {Id = 123});
}

Вы можете проверить, выполнив:

var jsonResult = controller.Test();
Assert.AreEqual("{Id = 123}", jsonResult.Data.ToString());

Я предпочитаю это решение так:

  • позволяет избежать изменения исходного кода (InternalsVisibleTo, ExpandoObject),
  • избегает использования MvcContrib и RhinoMocks (нет проблем с любым из них, но зачем добавлять только для тестирования JsonResult?) и
  • избегает использования Отражения (добавляет сложности к испытаниям).

Ответ 3

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