2013-06-01 9 views
25

Stavo provando a testare un metodo in uno dei miei controllori restituendo un JsonResult. Con mia grande sorpresa il seguente codice non ha funzionato:Asserzione di JsonResult contenente il tipo anonimo

[HttpPost] 
public JsonResult Test() { 
    return Json(new {Id = 123}); 
} 

Ecco come ho provarlo (si noti, inoltre, che il codice di prova risiede in un altro assembly):

// Act 
dynamic jsonResult = testController.Test().Data; 

// Assert 
Assert.AreEqual(123, jsonResult.Id); 

Il Assert genera un'eccezione:

'oggetto' non contiene una definizione per 'Id'

allora ho risolto esso utilizzando il seguente:

[HttpPost] 
public JsonResult Test() { 
    dynamic data = new ExpandoObject(); 
    data.Id = 123; 
    return Json(data); 
} 

sto cercando di capire il motivo per cui non è il primo lavoro? Sembra anche che funzioni fondamentalmente con MA un tipo anonimo.

+1

Ho provato il codice con il tipo anonimo e ha funzionato bene per me. Non sono sicuro del motivo per cui hai ricevuto quell'errore. –

+0

Cosa ottieni quando si stampa 'jsonResult.GetType()'? (l'errore indica che è di tipo 'object' piuttosto che di tipo' <> f__AnonymousType0', che è quello che mi aspetterei) –

+0

Il tipo è effettivamente oggetto. Mi aspettavo che funzionasse da solo, non sono sicuro del motivo per cui ottengo quei risultati. –

risposta

29

Per essere chiari, il problema specifico che si verifica è che C# dynamic non funziona con membri non pubblici. Questo è di design, presumibilmente per scoraggiare quel genere di cose. Dal momento che, come ha affermato LukLed, i tipi anonimi sono pubblici solo all'interno dello stesso assembly (o per essere più precisi, i tipi anonimi sono semplicemente contrassegnati internal, non public), si sta imbattendo in questa barriera.

Probabilmente la soluzione più pulita sarebbe quella di utilizzare InternalsVisibleTo. Ti consente di nominare un altro assembly che può accedere ai suoi membri non pubblici. Usarlo per i test è una delle ragioni principali della sua esistenza. Nel tuo esempio, si potrebbe mettere in AssemblyInfo.cs del vostro progetto primario la seguente riga:

[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")] 

Una volta fatto questo, l'errore andrà via (ho appena provato io stesso).

In alternativa, si potrebbe avere appena usato forza bruta riflessione:

Assert.AreEqual(123, jsonResult.GetType().GetProperty("Id").GetValue(jsonResult, null)); 
+0

Puoi spiegare l'ultima opzione? – Ani

+0

Puoi chiarire la tua domanda? Ti stai interrogando sulla riflessione in generale o su come si applica a questa domanda/risposta? –

+0

riflessione in generale – Ani

4

I tipi anonimi sono interni, quindi non è possibile esporli a un'altra libreria, quella con i test. Se hai inserito il codice di test nella stessa libreria del controller, funzionerà.

+0

Non ne avevo idea, grazie mille. Apprezzo la tua risposta. –

+1

Si potrebbe anche dichiarare l'assemblaggio di prova un "assemblaggio amico" per esporre i componenti interni. http://msdn.microsoft.com/en-us/library/0tke9fxk(v=vs.80).aspx –

10

Dopo aver letto le risposte qui e poi guardando più lontano, ho trovato un 2009 msdn blog post con un approccio ancora diverso. Ma .. nei commenti è stata una soluzione molto semplice e molto elegante da Kieran ... per utilizzare .ToString().

Nel tuo caso originale:

[HttpPost] 
public JsonResult Test() 
{ 
    return Json(new {Id = 123}); 
} 

Si potrebbe provare facendo:

var jsonResult = controller.Test(); 
Assert.AreEqual("{Id = 123}", jsonResult.Data.ToString()); 

preferisco di gran lunga questa soluzione in quanto:

  • evita modificando il codice originale (InternalsVisibleTo , ExpandoObject),
  • evita usando MvcContrib e RhinoMocks (nessun problema con uno di questi, ma perché aggiungere solo per essere in grado di testare JsonResult?), E, ​​
  • evita usando Riflessione (aggiunge complessità alle prove).