Утверждение 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);
}
Я пытаюсь понять, почему не первый работает? Он также, похоже, работает с чем угодно, но анонимным типом.
Ответы
Ответ 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
Анонимные типы являются внутренними, поэтому вы не можете выставить их в другую библиотеку, а те, в которых есть тесты. Если вы разместили тестовый код в той же библиотеке, что и контроллер, он будет работать.