2016-07-18 196 views
8

Ho problemi con l'unità test dei controller ASP.NET Core MVC che restituiscono oggetti anonimi. Il test dell'unità è impostato in un progetto separato e chiama direttamente i metodi del controllore dal progetto principale.Come si esegue il test dell'unità per i controller MVC di base ASP.NET che restituiscono oggetti anonimi?

I metodi del controller restituiscono IActionResult ma in genere si tratta di oggetti OkObjectResult e BadRequestObjectResult che vengono tradotti in una risposta JSON con il codice di stato HTTP appropriato. Gli oggetti anonimi vengono passati come parametri di costruzione per gli oggetti ObjectResult e sono questi che sto cercando di fare asserzioni contro (accessibile tramite ObjectResult.Value).

Ho trovato questa domanda (how can i access internals in asp.net 5), che ha una risposta che dice di usare la dinamica e aggiungere

[assembly: InternalsVisibleTo("Namespace")] 

per AssemblyInfo.cs per consentire l'accesso del progetto di prova per le proprietà degli oggetti interni degli oggetti anonimi. Tuttavia, le ultime versioni di ASP.NET Core MVC non hanno AssemblyInfo.cs e l'aggiunta di una come suggerito nelle risposte alla domanda collegata non funziona neanche.

C'è ora una posizione diversa per aggiungere InternalsVisibleTo o mi manca qualcosa?

+0

http : //stackoverflow.com/questions/9956648/how-do-i-check-if-a-property-exists-on-a-dynamic-anonymous-type-in-c –

risposta

16

Idea originale da this answer con un approccio più generico. Utilizzando un personalizzato DynamicObject come wrapper per il controllo del valore attraverso la riflessione non c'era bisogno di aggiungere il 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); 
    } 
} 

Supponendo che il seguente controller

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

test potrebbe apparire come

[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."); 
    } 
} 
+0

Questo è davvero un ele soluzione gant per i progetti ASP.NET Core MVC che non utilizzano più AssemblyInfo. Funziona perfettamente così l'ho accettato come risposta. – Jargon

+0

È fantastico! Mi dà il 97% di quello che mi serve :) Ora, circa il 3% ... È possibile ottenere la lunghezza di jsonCollection? e cosa servirebbe per ottenere, per esempio, jsonCollection [0], così posso affermare non solo NotNull, ma anche il valore attuale: 'Assert.Equal (4, jsonCollection.Count())' e 'Assert.Equal (25 , jsonCollection [0] .id) '. Se vuoi che inizi un nuovo thread, è felice di farlo! – Felix

+1

@Felix Aggiungi la discussione e fammi sapere. Ho lavorato per perfezionare questo lavoro da quando lo ho messo qui. Quando avrò la possibilità darò un'occhiata ai dettagli nel tuo commento. dalla cima della mia testa. per 'jsonCollection [0]' Sto pensando 'override TryGetIndex'. per il fatto che devi lanciare perché hai a che fare con 'dynamic' cioè 'Assert.Equal (4, (int) jsonCollection.Count())'. Ho cercato di farlo come te ma finora non ci sono dadi. il casting sembrava funzionare, quindi mi sono limitato a quello. – Nkosi

Problemi correlati