2016-07-23 8 views
3

Этот вопрос был вдохновлен этим excellent example. У меня есть приложение ASP.NET Core MVC, и я пишу unit tests для контроллера. Один из методов возвращает JsonResult с коллекцией анонимных типов. Я могу добраться до каждого элемента коллекции. Я могу также утверждать значения в каждом элементе следующим образом:Единичный тест для коллекции анонимных объектов JSON

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]) (это, очевидно, псевдокод).

+0

'.Count()' это метод расширения. Не думайте, что члены могут быть доступны/вызваны, если они не существуют непосредственно на обернутом объекте. Если объект имеет значение «ICollection», то может использоваться свойство «Count». Другой запрос возможен посредством переопределения 'TryGetIndex' и некоторого соответствия типов. – Nkosi

ответ

2

Вот обновленный вариант динамического объекта обертке.

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); 
    } 

} 
+0

Хмм .... Похоже, что Type.IsClass и Type.IsInterface недоступны в ASP.NET Core. Должен быть type.GetTypeInfo(). IsClass. http://stackoverflow.com/questions/35439749/how-to-check-if-a-type-is-abstract-in-net-core По-прежнему пытается выяснить, как вызвать InvokeMember() – Felix

+0

Где вы пытаетесь использовать Type.IsInterface? Также как и какой член вы пытаетесь вызвать? До сих пор самой сложной является метод isAnonymous. – Nkosi

+0

Nkosi пока я просто пытаюсь скомпилировать вашу оболочку динамического объекта;) Это то, что я нашел re: InvokeMember https://github.com/npgsql/npgsql/issues/471: InvokeMember -> Здесь нет простого эквивалента, Я хочу найти участника и использовать соответствующий метод вызова, основанный на значениях BindingFlags, используемых в исходном вызове (зависит от типа члена, как правило, не очень сложно.) - Я думаю, легко, чтобы парень Microsoft сказал :) – Felix

0

В случае, если простых утверждений и запросов Linq недостаточно, вы можете использовать методы CollectionAssert из mstest.

  • увидеть более подробное обсуждение по этому thread

 Смежные вопросы

  • Нет связанных вопросов^_^