Domanda

Uso Assert.Fail molto quando faccio TDD. Di solito lavoro su un test alla volta, ma quando ho idee per cose che voglio implementare in seguito scrivo rapidamente un test vuoto in cui il nome del metodo di test indica ciò che voglio implementare come una sorta di todo-list. Per essere sicuro di non dimenticare di aver inserito un Assert.Fail () nel corpo.

Quando ho provato xUnit.Net ho scoperto che non avevano implementato Assert.Fail. Ovviamente puoi sempre Assert.IsTrue (false) ma anche questo non comunica la mia intenzione. Ho avuto l'impressione Assert.Fail non è stato implementato apposta. Questa è considerata una cattiva pratica? Se è così, perché?


@Martin Meredith Non è esattamente quello che faccio. Scrivo prima un test e poi implemento il codice per farlo funzionare. Di solito penso a diversi test contemporaneamente. Oppure penso a un test da scrivere quando sto lavorando a qualcos'altro. Questo è quando scrivo un vuoto test fallito da ricordare. Quando riesco a scrivere il test, lavoro in modo accurato prima di tutto.

@Jimmeh Sembra una buona idea. I test ignorati non falliscono ma vengono comunque visualizzati in un elenco separato. Devo provarlo.

@Matt Howells Grande idea. NotImplementedException comunica l'intenzione meglio di assert.Fail () in questo caso

@Mitch Wheat Questo è quello che stavo cercando. Sembra che sia stato lasciato fuori per impedire che venga abusato in un altro modo in cui lo abuso.

È stato utile?

Soluzione

Per questo scenario, anziché chiamare Assert.Fail, faccio quanto segue (in C # / NUnit)

[Test]
public void MyClassDoesSomething()
{
    throw new NotImplementedException();
}

È più esplicito di un Assert.Fail.

Sembra esserci un accordo generale sul fatto che sia preferibile utilizzare asserzioni più esplicite di Assert.Fail (). La maggior parte dei framework deve includerlo perché non offre un'alternativa migliore. Ad esempio, NUnit (e altri) forniscono un ExpectedExceptionAttribute per verificare che alcuni codici generino una particolare classe di eccezioni. Tuttavia, al fine di verificare che una proprietà sull'eccezione sia impostata su un valore particolare, non è possibile utilizzarla. Invece devi ricorrere ad Assert.Fail:

[Test]
public void ThrowsExceptionCorrectly()
{
    const string BAD_INPUT = "bad input";
    try
    {
        new MyClass().DoSomething(BAD_INPUT);
        Assert.Fail("No exception was thrown");
    }
    catch (MyCustomException ex)
    {
         Assert.AreEqual(BAD_INPUT, ex.InputString); 
    }
}

Il metodo xUnit.Net Assert.Throws rende questo molto più ordinato senza richiedere un metodo Assert.Fail. Non includendo un metodo Assert.Fail () xUnit.Net incoraggia gli sviluppatori a trovare e utilizzare alternative più esplicite e a supportare la creazione di nuove asserzioni ove necessario.

Altri suggerimenti

È stato deliberatamente escluso. Questa è la risposta di Brad Wilson sul perché non esiste Assert.Fail ():

  

In realtà non l'abbiamo trascurato. io   trovare Assert.Fail è una stampella che   implica che probabilmente c'è un   asserzione mancante. A volte è giusto   il modo in cui è strutturato il test e   a volte è perché Assert potrebbe   usa un'altra affermazione.

Ho sempre usato Assert.Fail () per gestire i casi in cui hai rilevato che un test dovrebbe fallire attraverso la logica oltre il semplice confronto di valori. Ad esempio:

try 
{
  // Some code that should throw ExceptionX
  Assert.Fail("ExceptionX should be thrown")
} 
catch ( ExceptionX ex ) 
{
  // test passed
}

Quindi la mancanza di Assert.Fail () nel framework mi sembra un errore. Suggerirei di correggere la classe Assert in modo da includere un metodo Fail () e di inoltrare la patch agli sviluppatori del framework, insieme al tuo ragionamento per aggiungerlo.

Per quanto riguarda la tua pratica di creare test che falliscono intenzionalmente nel tuo spazio di lavoro, ricordare a te stesso di implementarli prima di impegnarti, mi sembra una buona pratica.

Uso MbUnit per il mio test unitario. Hanno un'opzione per Ignora i test, che vengono visualizzati come Arancione (anziché Verde o Rosso) nella suite di test. Forse xUnit ha qualcosa di simile e significherebbe che non devi nemmeno inserire alcuna affermazione nel metodo, perché si presenterebbe in un colore fastidiosamente diverso che rende difficile perdere?

Modifica:

In MbUnit è nel modo seguente:

[Test]
[Ignore]
public void YourTest()
{ } 

Questo è il modello che uso quando scrivo un test per il codice che voglio generare un'eccezione in base alla progettazione:

[TestMethod]
public void TestForException()
{
    Exception _Exception = null;

    try
    {
        //Code that I expect to throw the exception.
        MyClass _MyClass = null;
        _MyClass.SomeMethod();
        //Code that I expect to throw the exception.
    }
    catch(Exception _ThrownException)
    {   
        _Exception = _ThrownException
    }
    finally
    {
        Assert.IsNotNull(_Exception);
        //Replace NullReferenceException with expected exception.
        Assert.IsInstanceOfType(_Exception, typeof(NullReferenceException));
    }
}

IMHO è un modo migliore di testare le eccezioni sull'uso di Assert.Fail (). La ragione di ciò è che non solo collaudo un'eccezione, ma provo anche il tipo di eccezione. Mi rendo conto che questo è simile alla risposta di Matt Howells, ma IMHO che utilizza il blocco finally è più robusto.

Ovviamente sarebbe ancora possibile includere altri metodi Assert per testare la stringa di input delle eccezioni ecc. Sarei grato per i vostri commenti e opinioni sul mio schema.

Personalmente non ho alcun problema con l'uso di una suite di test come todo list come questa purché alla fine si riesca a scrivere il test prima di implementare il codice da passare.

Detto questo, usavo questo approccio da solo, anche se ora sto scoprendo che farlo mi porta su un percorso di scrittura di troppi test in anticipo, che in un modo strano è come il problema inverso di non scrivere test affatto: finisci per prendere decisioni sulla progettazione un po 'troppo presto IMHO.

Per inciso in MSTest, il modello di test standard utilizza Assert.Inclusivo alla fine dei suoi campioni.

AFAIK il framework xUnit.NET è pensato per essere estremamente leggero e sì, hanno tagliato deliberatamente Fail, per incoraggiare lo sviluppatore a utilizzare una condizione di errore esplicita.

Indovina selvaggia: trattenere Assert.Fail ha lo scopo di impedirti di pensare che un buon modo per scrivere il codice di prova sia come un enorme mucchio di spaghetti che porta ad un Assert.Fail nei casi cattivi. [Modifica per aggiungere: le risposte degli altri lo confermano ampiamente, ma con citazioni]

Dato che non è quello che stai facendo, è possibile che xUnit.Net sia iperprotettivo.

O forse pensano solo che sia così raro e così poco ortogonale da non essere necessario.

Preferisco implementare una funzione chiamata ThisCodeHasNotBeenWrittenYet (in realtà qualcosa di più corto, per facilitare la digitazione). Non riesco a comunicare l'intenzione più chiaramente di così, e hai un termine di ricerca preciso.

Se ciò non riesce o non viene implementato (per provocare un errore del linker) o se una macro non viene compilata, può essere modificata in base alle proprie preferenze correnti. Ad esempio, quando si desidera eseguire qualcosa che è finito, si desidera un errore. Quando ti siedi per sbarazzartene, potresti volere un errore di compilazione.

Con il buon codice di solito faccio:

void goodCode() {
     // TODO void goodCode()
     throw new NotSupportedOperationException("void goodCode()");
}

Con il codice di prova di solito faccio:

@Test
void testSomething() {
     // TODO void test Something
     Assert.assert("Some descriptive text about what to test")
}

Se si utilizza JUnit e non si desidera ottenere l'errore, ma l'errore, di solito lo faccio:

@Test
void testSomething() {
     // TODO void test Something
     throw new NotSupportedOperationException("Some descriptive text about what to test")
}

Fai attenzione a Assert.Fail e alla sua influenza corruttiva per indurre gli sviluppatori a scrivere test stupidi o rotti. Ad esempio:

[TestMethod]
public void TestWork()
{
    try {
        Work();
    }
    catch {
        Assert.Fail();
    }
}

Questo è sciocco, perché il try-catch è ridondante. Un test ha esito negativo se genera un'eccezione.

Anche

[TestMethod]
public void TestDivide()
{
    try {
        Divide(5,0);
        Assert.Fail();
    } catch { }
}

Questo è rotto, il test passerà sempre qualunque sia l'esito della funzione Divide. Ancora una volta, un test fallisce se e solo se genera un'eccezione.

Perché dovresti usare Assert.Fail per dire che dovrebbe essere generata un'eccezione? Questo non è necessario. Perché non usare solo l'attributo ExpectedException?

Se stai scrivendo un test che fallisce e poi scrivi il codice per esso, quindi scrivi il test. Questo non è Test Driven Development.

Tecnicamente, Assert.fail () non dovrebbe essere necessario se stai usando correttamente lo sviluppo guidato dai test.

Hai mai pensato di usare un Elenco Todo o applicare una metodologia GTD al tuo lavoro?

MS Test ha Assert.Fail () ma ha anche Assert.Inconclusive () . Penso che l'uso più appropriato per Assert.Fail () sia se si dispone di una logica in linea che sarebbe scomoda da inserire in un'asserzione, anche se non riesco nemmeno a pensare a nessun buon esempio. Per la maggior parte, se il framework di test supporta qualcosa di diverso da Assert.Fail (), allora usalo.

Penso che dovresti chiederti cosa dovrebbero fare i test (iniziali).

Per prima cosa, scrivi un (set di) test senza impianto. Forse, anche gli scenari dei giorni di pioggia.

Tutti questi test devono fallire, per essere corretti: Quindi vuoi raggiungere due cose: 1) Verifica che l'implementazione sia corretta; 2) Verifica che i test unitari siano corretti.

Ora, se fai TDD in anticipo, vuoi eseguire tutti i tuoi test, anche le parti della NYI. Il risultato della prova totale ha esito positivo se: 1) Tutte le cose implementate hanno successo 2) Tutte le cose della NYI falliscono

Dopotutto, sarebbe un ommision di unit test se i tuoi test di unità hanno successo mentre non c'è implementazione, non è vero?

Vuoi finire con qualcosa di simile a una mail del tuo test di integrazione continua che controlla tutto il codice implementato e non implementato e viene inviato se un codice implementato fallisce o se un codice non implementato ha esito positivo. Entrambi sono risultati indesiderati.

Basta scrivere un [ignora] test non farà il lavoro. Né, un'asserzione che interrompe un primo errore di asserzione, non l'esecuzione di altre linee di test nel test.

Ora, come ottenerlo allora? Penso che richieda un'organizzazione più avanzata dei tuoi test. E richiede qualche altro meccanismo, quindi afferma di raggiungere questi obiettivi.

Penso che devi dividere i test e creare alcuni test che vengono eseguiti completamente ma devono fallire e viceversa.

Le idee sono di dividere i test su più assiemi, utilizzare il raggruppamento di test (i test ordinati in mstest possono fare il lavoro).

Tuttavia, una build CI che spedisce se non tutti i test nel dipartimento di New York falliscono non è facile e diretta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top