如何对返回匿名对象的ASP.NET Core MVC控制器进行单元测试?
我无法对返回匿名对象的ASP.NET Core MVC控制器进行单元测试.单元测试是在一个单独的项目中设置的,并直接从主项目中调用控制器方法.
I'm having trouble unit testing ASP.NET Core MVC controllers that return anonymous objects. The unit testing is set up in a separate project and calls the controller methods from the main project directly.
控制器方法返回IActionResult
,但是通常这些是OkObjectResult
和BadRequestObjectResult
对象,它们被转换为带有适当HTTP状态代码的JSON响应.匿名对象作为ObjectResult
对象的构造函数参数传递,正是我要针对这些对象进行断言(可通过ObjectResult.Value
访问).
The controller methods return IActionResult
but typically these are OkObjectResult
and BadRequestObjectResult
objects that get translated into a JSON response with the appropriate HTTP status code. The anonymous objects are passed as the constructor parameters for the ObjectResult
objects and it is these I'm trying to make assertions against (accessible via ObjectResult.Value
).
我发现了这个问题(如何访问asp.net 5 )的答案是使用动态和添加
I found this question (how can i access internals in asp.net 5) that has an answer that says to use dynamics and add
[assembly: InternalsVisibleTo("Namespace")]
to AssemblyInfo.cs to allow the test project access to the internal object properties of the anonymous objects. However, latest versions of ASP.NET Core MVC do not have AssemblyInfo.cs and adding one as suggested in the answers to the linked question does not work either.
现在是否有其他位置可以添加InternalsVisibleTo
或我缺少什么?
Is there now a different location to add the InternalsVisibleTo
or am I missing something?
来自此答案的原始想法,具有更通用的含义方法.使用自定义DynamicObject
作为包装通过反射检查值的方法,无需添加InternalsVisibleTo
Original idea from this answer with a more generic approach. Using a custom DynamicObject
as a wrapper for inspecting the value via reflection there was no need to add the InternalsVisibleTo
public class DynamicObjectResultValue : DynamicObject, IEquatable<DynamicObjectResultValue> {
private readonly object value;
public DynamicObjectResultValue(object value) {
this.value = value;
}
#region Operators
public static bool operator ==(DynamicObjectResultValue a, DynamicObjectResultValue b) {
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b)) {
return true;
}
// If one is null, but not both, return false.
if (ReferenceEquals((object)a, null) || ReferenceEquals((object)b, null)) {
return false;
}
// Return true if the fields match:
return a.value == b.value;
}
public static bool operator !=(DynamicObjectResultValue a, DynamicObjectResultValue b) {
return !(a == b);
}
#endregion
public override IEnumerable<string> GetDynamicMemberNames() {
return value.GetType().GetProperties().Select(p => p.Name);
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
//initialize value
result = null;
//Search possible matches and get its value
var property = value.GetType().GetProperty(binder.Name);
if (property != null) {
// If the property is found,
// set the value parameter and return true.
var propertyValue = property.GetValue(value, null);
result = propertyValue;
return true;
}
// Otherwise, return false.
return false;
}
public override bool Equals(object obj) {
if (obj is DynamicObjectResultValue)
return Equals(obj as DynamicObjectResultValue);
// If parameter is null return false.
if (ReferenceEquals(obj, null)) return false;
// Return true if the fields match:
return this.value == obj;
}
public bool Equals(DynamicObjectResultValue other) {
// If parameter is null return false.
if (ReferenceEquals(other, null)) return false;
// Return true if the fields match:
return this.value == other.value;
}
public override int GetHashCode() {
return ToString().GetHashCode();
}
public override string ToString() {
return string.Format("{0}", value);
}
}
假设使用以下控制器
public class FooController : Controller {
public IActionResult GetAnonymousObject() {
var jsonResult = new {
id = 1,
name = "Foo",
type = "Bar"
};
return Ok(jsonResult);
}
public IActionResult GetAnonymousCollection() {
var jsonResult = Enumerable.Range(1, 20).Select(x => new {
id = x,
name = "Foo" + x,
type = "Bar" + x
}).ToList();
return Ok(jsonResult);
}
}
测试看起来像
[TestMethod]
public void TestDynamicResults() {
//Arrange
var controller = new FooController();
//Act
var result = controller.GetAnonymousObject() as OkObjectResult;
//Assert
dynamic obj = new DynamicObjectResultValue(result.Value);
Assert.IsNotNull(obj);
Assert.AreEqual(1, obj.id);
Assert.AreEqual("Foo", obj.name);
Assert.AreEqual(3, obj.name.Length);
Assert.AreEqual("Bar", obj.type);
}
[TestMethod]
public void TestDynamicCollection() {
//Arrange
var controller = new FooController();
//Act
var result = controller.GetAnonymousCollection() as OkObjectResult;
//Assert
Assert.IsNotNull(result, "No ActionResult returned from action method.");
dynamic jsonCollection = result.Value;
foreach (dynamic value in jsonCollection) {
dynamic json = new DynamicObjectResultValue(value);
Assert.IsNotNull(json.id,
"JSON record does not contain \"id\" required property.");
Assert.IsNotNull(json.name,
"JSON record does not contain \"name\" required property.");
Assert.IsNotNull(json.type,
"JSON record does not contain \"type\" required property.");
}
}