Come posso verificare un'eccezione prevista con un messaggio di eccezione specifico da un file di risorse in Visual Studio Test?

StackOverflow https://stackoverflow.com/questions/113395

Domanda

Il test di Visual Studio può verificare le eccezioni previste utilizzando l'attributo ExpectedException. Puoi passare un'eccezione come questa:

[TestMethod]
[ExpectedException(typeof(CriticalException))]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

Puoi anche verificare il messaggio contenuto in ExpectedException in questo modo:

[TestMethod]
[ExpectedException(typeof(CriticalException), "An error occured")]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

Ma quando testare le applicazioni I18N vorrei usare un file di risorse per ottenere quel messaggio di errore (qualcuno potrebbe anche decidere di testare le diverse localizzazioni del messaggio di errore se lo desidero, ma Visual Studio non mi lascerà fare questo:

[TestMethod]
[ExpectedException(typeof(CriticalException), MyRes.MultipleOrganisationsNotAllowed)]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

Il compilatore genererà il seguente errore:

  

Un argomento di attributo deve essere a   espressione costante, tipo di espressione   o espressione di creazione di array di un   attributo

Qualcuno sa come verificare un'eccezione che contiene un messaggio da un file di risorse?


Un'opzione che ho preso in considerazione è l'utilizzo di classi di eccezione personalizzate, ma basate su consigli spesso ascoltati come:

  

" Crea e genera eccezioni personalizzate   se hai una condizione di errore che   può essere gestito a livello di programmazione in a   modo diverso rispetto a qualsiasi altro esistente   eccezione. Altrimenti, lancia uno dei   eccezioni esistenti. " Fonte

Non mi aspetto di gestire le eccezioni in modo diverso nel flusso normale (è un'eccezione critica, quindi vado comunque in modalità panico) e non penso che creare un'eccezione per ogni caso di test sia la cosa giusta da fare. Qualche opinione?

È stato utile?

Soluzione

Solo un'opinione, ma direi il testo dell'errore:

  • fa parte del test, nel qual caso ottenerlo dalla risorsa sarebbe "sbagliato" (altrimenti potresti finire con una risorsa costantemente alterata), quindi aggiorna il test quando cambi la risorsa (o il test fallisce )
  • non fa parte del test e dovresti solo preoccuparti che genera l'eccezione.

Nota che la prima opzione dovrebbe permetterti di testare più lingue, data la possibilità di funzionare con una localizzazione.

Per quanto riguarda le eccezioni multiple, vengo dalla terra C ++, dove la creazione di un sacco di eccezioni (al punto di un'istruzione per "lancio"!) nelle grandi erede è accettabile (se non comune), ma i metadati di .Net al sistema probabilmente non piace, quindi questo consiglio.

Altri suggerimenti

Consiglierei di usare un metodo helper invece di un attributo. Qualcosa del genere:

public static class ExceptionAssert
{
  public static T Throws<T>(Action action) where T : Exception
  {
    try
    {
      action();
    }
    catch (T ex)
    {
      return ex;
    }
    Assert.Fail("Exception of type {0} should be thrown.", typeof(T));

    //  The compiler doesn't know that Assert.Fail
    //  will always throw an exception
    return null;
  }
}

Quindi puoi scrivere il tuo test in questo modo:

[TestMethod]
public void GetOrganisation_MultipleOrganisations_ThrowsException()
{
  OrganizationList organizations = new Organizations();
  organizations.Add(new Organization());
  organizations.Add(new Organization());

  var ex = ExceptionAssert.Throws<CriticalException>(
              () => organizations.GetOrganization());
  Assert.AreEqual(MyRes.MultipleOrganisationsNotAllowed, ex.Message);
}

Questo ha anche il vantaggio di verificare che l'eccezione venga generata sulla linea in cui ti aspettavi che venisse lanciata invece che ovunque nel tuo metodo di test.

L'argomento del messaggio ExpectedException non corrisponde al messaggio dell'eccezione. Piuttosto questo è il messaggio che viene stampato nei risultati del test se l'eccezione prevista non si è effettivamente verificata.

Penso che puoi semplicemente fare un try-catch esplicito nel tuo codice di test invece di fare affidamento sull'attributo ExpectedException per farlo per te. Quindi puoi trovare un metodo di supporto che leggerà il file di risorse e confronterà il messaggio di errore con quello fornito con l'eccezione rilevata. (ovviamente se non ci fosse un'eccezione, il test case dovrebbe essere considerato un fallimento)

Se passi all'utilizzo della molto bella libreria di test xUnit.Net , puoi sostituire [ExpectedException] con qualcosa del genere:

[Fact]
public void TestException()
{
   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.
}

Mi chiedo se NUnit si sta muovendo lungo il sentiero lontano dalla semplicità ... ma ecco qua.

I nuovi miglioramenti (2.4.3 e versioni successive?) dell'attributo ExpectedException ti consentono un maggiore controllo sui controlli da eseguire sull'eccezione prevista tramite un metodo Handler . Maggiori dettagli sulla pagina ufficiale del documento NUnit .. verso il fine della pagina.

[ExpectedException( Handler="HandlerMethod" )]
public void TestMethod()
{
...
}

public void HandlerMethod( System.Exception ex )
{
...
}

Nota: qualcosa non si sente proprio qui. Perché i messaggi delle eccezioni sono internazionalizzati. Stai utilizzando le eccezioni per le cose che devono essere gestite o notificate all'utente. A meno che tu non abbia un sacco di sviluppatori culturalmente diversi che correggono i bug .. non dovresti averne bisogno. Sarebbero sufficienti eccezioni in inglese o in una lingua comunemente accettata. Ma nel caso tu debba avere questo ... è possibile :)

Mi sono imbattuto in questa domanda mentre provavo a risolvere un problema simile da solo. (Descriverò in dettaglio la soluzione che ho deciso di seguito.)

Devo essere d'accordo con i commenti di Gishu sull'internazionalizzazione dei messaggi di eccezione essendo un odore di codice.

Inizialmente l'avevo fatto nel mio progetto in modo che potessi avere coerenza tra i messaggi di errore generati dalla mia applicazione e i test delle mie unità. cioè, per definire solo i miei messaggi di eccezione in un posto e al momento, il file di risorse sembrava un posto ragionevole per farlo poiché lo stavo già usando per varie etichette e stringhe (e dal momento che aveva senso aggiungere un riferimento ad esso nel mio codice di prova per verificare che quelle stesse etichette siano mostrate nei punti appropriati).

Ad un certo punto avevo considerato (e testato) l'uso dei blocchi try / catch per evitare il requisito di una costante dall'attributo ExpectedException, ma questo sembrava che avrebbe portato a un sacco di codice extra se applicato su larga scala .

Alla fine, la soluzione su cui ho optato era quella di creare una classe statica nella mia libreria di risorse e memorizzare i miei messaggi di eccezione in quella. In questo modo non è necessario internazionalizzarli (che concordo non ha senso) e sono resi accessibili ogni volta che una stringa di risorse sarebbe accessibile poiché si trovano nello stesso spazio dei nomi. (Ciò si adatta al mio desiderio di non rendere la verifica del testo dell'eccezione un processo complesso.)

Il mio codice di prova quindi si riduce semplicemente a (scusate il mangling ...):

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
} 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top