匿名JSON对象集合的单元测试

本文关键字:单元测试 集合 对象 JSON 匿名 | 更新日期: 2023-09-27 17:58:18

这个问题的灵感来自这个优秀的例子。我有ASP.NET核心MVC应用程序,我正在为控制器编写单元测试。其中一个方法返回带有匿名类型集合的JsonResult。我可以找到集合中的每一个元素。我还可以在每个元素中断言值,如下所示:

Dictionary<int, string> expectedValues = new Dictionary<int, string> {
    { 1, "Welcome Tester"},
    { 2, "Namaste Tester"},
    { 3, "Privet Tester"},
    { 4, "Labdien Tester"}
};
foreach (dynamic value in jsonCollection) {
    dynamic json = new DynamicObjectResultValue(value);
    Assert.Equal(expectedValues[json.Id], json.Greeting);
}

但是,有没有一种方法可以对整个收藏做出断言?例如,Assert.Equal(4, jsonCollection.Count())Assert.Contains(2, jsonCollection[Id])(这显然是伪代码)。

匿名JSON对象集合的单元测试

这里是动态对象包装器的更新版本。

public static class DynamicObjectWrapperExtension {
    /// <summary>
    /// Return provided object as a <seealso cref="System.Dynamic.DynamicObject"/>
    /// </summary>  
    public static dynamic AsDynamicObject(this object value) {
        return new DynamicObjectWrapper(value);
    }
}
public class DynamicObjectWrapper : DynamicObject, IEquatable<DynamicObjectWrapper> {
    private readonly object value;
    private readonly Type valueType;
    public DynamicObjectWrapper(object value) {
        this.value = value;
        this.valueType = value.GetType();
    }
    public override IEnumerable<string> GetDynamicMemberNames() {
        return valueType.GetProperties().Select(p => p.Name);
    }
    public override bool TryConvert(ConvertBinder binder, out object result) {
        result = null;
        try {
            result = changeTypeCore(value, binder.Type);
        } catch {
            return false;
        }
        return true;
    }
    private object changeTypeCore(object value, Type convertionType) {
        if (ReferenceEquals(value, null))
            return getDefaultValueForType(convertionType);
        var providedType = valueType;
        if (convertionType.IsAssignableFrom(providedType)) {
            return value;
        }
        try {
            var converter = TypeDescriptor.GetConverter(convertionType);
            if (converter.CanConvertFrom(providedType)) {
                return converter.ConvertFrom(value);
            }
            converter = TypeDescriptor.GetConverter(providedType);
            if (converter.CanConvertTo(providedType)) {
                return converter.ConvertTo(value, convertionType);
            }
        } catch {
            return value;
        }
        try {
            return Convert.ChangeType(value, convertionType, System.Globalization.CultureInfo.CurrentCulture);
        } catch {
            return value;
        }
    }
    private object getDefaultValueForType(Type targetType) {
        return targetType.IsClass || targetType.IsInterface ? null : Activator.CreateInstance(targetType);
    }
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
        result = null;
        //1d collection
        if (potentialIndex(indexes)) {
            int index = (int)indexes[0];
            var list = value as IList;
            if (validIndex(index, list)) {
                result = checkValue(list[index]);
                return true;
            }
        }
        return false;
    }
    private bool validIndex(int index, IList list) {
        return index >= 0 && index < list.Count;
    }
    private bool potentialIndex(object[] indexes) {
        return indexes[0] != null && typeof(int) == indexes[0].GetType() && value is IList;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        return TryGetValue(binder.Name, out result);
    }
    public bool TryGetValue(string propertyName, out object result) {
        result = null;
        var property = valueType.GetProperty(propertyName);
        if (property != null) {
            var propertyValue = property.GetValue(value, null);
            result = checkValue(propertyValue);
            return true;
        }
        return false;
    }
    private object checkValue(object value) {
        var valueType = value.GetType();
        return isAnonymousType(valueType)
            ? new DynamicObjectWrapper(value)
            : value;
    }
    private bool isAnonymousType(Type type) {
        //HACK: temporary hack till a proper function can be implemented
        return type.Namespace == null &&
            type.IsGenericType &&
            type.IsClass &&
            type.IsSealed &&
            type.IsPublic == false;
    }
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        try {
            result = valueType.InvokeMember(
                binder.Name,
                BindingFlags.InvokeMethod |
                BindingFlags.Public |
                BindingFlags.Instance,
                null, value, args);
            return true;
        } catch {
            result = null;
            return false;
        }
    }
    public override bool Equals(object obj) {
        // If parameter is null return false.
        if (ReferenceEquals(obj, null)) return false;
        // Return true if the fields match:
        return this.value == obj || (obj is DynamicObjectWrapper && Equals(obj as DynamicObjectWrapper));
    }
    public bool Equals(DynamicObjectWrapper 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() {
        var name = GetType().Name;
        return string.Format("{0}[{1}]", name, value);
    }
}

假设以下控制器

public class FooController : Controller {
    public IActionResult GetAnonymousObject() {
        var jsonResult = new {
            id = 1,
            name = "Foo",
            type = "Bar"
        };
        return Json(jsonResult);
    }
    public IActionResult GetAnonymousCollection() {
        var jsonResult = Enumerable.Range(1, 20).Select(x => new {
            id = x,
            name = "Foo" + x,
            type = "Bar" + x
        }).ToList();
        return Json(jsonResult);
    }
}

的使用示例

[TestClass]
public class DynamicObjectWrapperTests {
    [TestMethod]
    public void DynamicObjectResultValue_Member_Should_Exist() {
        //Arrange
        var controller = new FooController();
        //Act
        var result = controller.GetAnonymousObject() as JsonResult;
        //Assert
        dynamic obj = result.Value.AsDynamicObject();
        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 DynamicObjectResultValue_DynamicCollection() {
        //Arrange
        var controller = new FooController();
        //Act
        var result = controller.GetAnonymousCollection() as JsonResult;
        //Assert
        dynamic jsonCollection = result.Value;
        foreach (object value in jsonCollection) {
            dynamic json = value.AsDynamicObject();
            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.");
        }
    }
    [TestMethod]
    public void DynamicObjectResultValue_DynamicCollection_Should_Convert_To_IEnumerable() {
        //Arrange
        var controller = new FooController();
        //Act
        var result = controller.GetAnonymousCollection() as JsonResult;
        dynamic jsonCollection = result.Value.AsDynamicObject();
        int count = 0;
        foreach (var value in jsonCollection) {
            count++;
        }
        //Assert
        Assert.IsTrue(count > 0);
    }
    [TestMethod]
    public void DynamicObjectResultValue_DynamicCollection_Index_at_0_Should_Not_be_Null() {
        //Arrange
        var controller = new FooController();
        //Act
        var result = controller.GetAnonymousCollection() as JsonResult;
        dynamic jsonCollection = result.Value.AsDynamicObject();
        //Assert                
        Assert.IsNotNull(jsonCollection[0]);
    }
    [TestMethod]
    public void DynamicObjectResultValue_DynamicCollection_Should_Be_Indexable() {
        //Arrange
        var controller = new FooController();
        //Act
        var result = controller.GetAnonymousCollection() as JsonResult;
        dynamic jsonCollection = result.Value.AsDynamicObject();
        //Assert
        for (var i = 0; i < jsonCollection.Count; i++) {
            var json = jsonCollection[i];
            Assert.IsNotNull(json);
            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.");
        }
    }
    [TestMethod]
    public void DynamicObjectResultValue_DynamicCollection_Count_Should_Be_20() {
        //Arrange
        var controller = new FooController();
        //Act
        var result = controller.GetAnonymousCollection() as JsonResult;
        //Assert
        dynamic jsonCollection = result.Value.AsDynamicObject();
        Assert.AreEqual(20, jsonCollection.Count);
    }
}

如果简单的assert和Linq查询还不够,可以使用mstest中的CollectionAssert方法。

  • 查看此线程上的更多讨论