Domanda

Abbiamo alcuni NUnit test di accesso al database.Quando uno di loro non si può lasciare database in stato incoerente - che non è un problema, dal momento che abbiamo ricostruire il database per ogni esecuzione di test - ma può causare altri test per fallire nella stessa seduta.

È possibile rilevare che uno dei test fallito e di eseguire una sorta di pulizia?

Non vogliamo scrivere il codice di pulizia in tutti i test, abbiamo già farlo ora.Vorrei eseguire la pulizia in Demolizione, ma solo se il test è fallito, come la rimozione potrebbe essere costoso.

Aggiornamento:Per chiarire, vorrei test per essere semplice e NON includono la pulizia o la gestione degli errori di logica.Anche io non voglio per eseguire database di reimpostare ogni test eseguito solo se il test ha esito negativo.E questo codice dovrebbe probabilmente essere eseguito in Teardown metodo, ma io non sono a conoscenza di un modo per ottenere informazioni se il test attualmente stiamo abbattendo dal fallito o è stato un successo.

Update2:

        [Test]
        public void MyFailTest()
        {
            throw new InvalidOperationException();
        }

        [Test]
        public void MySuccessTest()
        {
            Assert.That(true, Is.True);
        }

        [TearDown]
        public void CleanUpOnError()
        {
            if (HasLastTestFailed()) CleanUpDatabase();
        }

Sto cercando di attuazione di HasLastTestFailed()

È stato utile?

Soluzione

Questa idea mi ha interessato, così ho fatto un po ' di scavo.NUnit non hanno questa capacità, fuori dalla scatola, ma c'è tutto un extensibility framework fornito con NUnit.Ho trovato questo articolo grande estensione di NUnit - e ' stato un buon punto di partenza.Dopo aver giocato intorno con esso, mi si avvicinò con la seguente soluzione:un metodo decorato con una custom CleanupOnError attributo sarà chiamato se una delle prove che l'attrezzatura non è riuscito.

Qui è come il test si presenta come:

  [TestFixture]
  public class NUnitAddinTest
  {
    [CleanupOnError]
    public static void CleanupOnError()
    {
      Console.WriteLine("There was an error, cleaning up...");
      // perform cleanup logic
    }

    [Test]
    public void Test1_this_test_passes()
    {
      Console.WriteLine("Hello from Test1");
    }

    [Test]
    public void Test2_this_test_fails()
    {
      throw new Exception("Test2 failed");
    }

    [Test]
    public void Test3_this_test_passes()
    {
      Console.WriteLine("Hello from Test3");
    }
  }

in cui l'attributo è semplicemente:

  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
  public sealed class CleanupOnErrorAttribute : Attribute
  {
  }

E qui è come è eseguito dal addin:

public void RunFinished(TestResult result)
{
  if (result.IsFailure)
  {
    if (_CurrentFixture != null)
    {
      MethodInfo[] methods = Reflect.GetMethodsWithAttribute(_CurrentFixture.FixtureType,
                                                             CleanupAttributeFullName, false);
      if (methods == null || methods.Length == 0)
      {
        return;
      }

      Reflect.InvokeMethod(methods[0], _CurrentFixture);
    }
  }
}

Ma ecco la parte difficile:il componente deve essere messo in addins directory accanto a NUnit runner.La mia era posto accanto al NUnit concorrente, in TestDriven.NET directory:

C:\Program Files\TestDriven.NET 2.0\NUnit\addins

(Ho creato l' addins directory, non c'era)

MODIFICA Un'altra cosa è che il metodo di pulitura deve essere static!

Ho messo insieme un semplice componente aggiuntivo, è possibile scaricare il sorgente da il mio SkyDrive.Sarà necessario aggiungere i riferimenti a nunit.framework.dll, nunit.core.dll e nunit.core.interfaces.dll nei luoghi appropriati.

Un paio di note:L'attributo di classe può essere posizionato in qualsiasi punto del codice.Non volevo inserirlo nella stessa assemblea, in quanto il componente stesso, perché si riferisce a due Core NUnit assemblee, così ho messo in un altro assieme.Basta ricordarsi di cambiare la linea in CleanAddin.cs, se si decide di metterlo altrove.

Speranza che aiuta.

Altri suggerimenti

A partire dalla versione 2.5.7, NUnit permette di Demolizione per rilevare se l'ultimo test fallito.Un nuovo TestContext classe permette di test per accedere alle informazioni su di loro tra cui il TestStauts.

Per ulteriori dettagli, si prega di consultare http://nunit.org/?p=releaseNotes&r=2.5.7

[TearDown]
public void TearDown()
{
    if (TestContext.CurrentContext.Result.Status == TestStatus.Failed)
    {
        PerformCleanUpFromTest();
    }
}

Mentre potrebbe essere possibile costringere nUnit per fare questo non è più ragionevole di design, si può sempre impostare un file temporaneo da qualche parte e se quel file esiste, di eseguire la pulizia.

Vorrei consigliamo di cambiare il codice in modo che si dispone di database-operazioni abilitato e alla fine del test, è sufficiente ripristinare il database allo stato originale (es.scartare la transazione che rappresenta l'unità-test).

Sì, c'è.È possibile utilizzare il Teardown attributo che teardown dopo ogni test.Si vuole applicare il Database di "reset" script e di demolizione e ri-installazione prima e dopo ogni test.

Questo attributo viene utilizzato all'interno di un TestFixture per fornire un insieme comune di le funzioni che vengono eseguite dopo ogni metodo di test è in esecuzione.

Aggiornamento:Sulla base dei commenti e di aggiornamento alla domanda, direi che è possibile utilizzare il teardown di attributo e l'uso privato variabili per indicare se il metodo contenuto del fuoco.

Però, ho fatto anche vedere che non vuoi nessun tipo di logica complessa o codice di gestione degli errori.

Dato che, mi piacerebbe pensare che una Configurazione standard/Teardown avrebbe funzionato meglio per voi.Non importa se c'è un errore e di non avere alcun codice di gestione degli errori.

Se avete bisogno di speciale clean-up, perché i prossimi test dipende dal completamento della corrente di prova, io suggerirei di rivedere i test -- che probabilmente non dovrebbe dipendere l'uno dall'altro.

Cosa sull'utilizzo di un blocco Try-Catch, generazione ripetuta eccezione catturato?

try
{
//Some assertion
}
catch
{
     CleanUpMethod();
     throw;
}

Vorrei fare come phsr suggerisce, per ora, e quando te lo puoi permettere, il refactoring del test in modo che non devono mai fare affidamento sugli stessi dati che un altro test bisogni o meglio ancora abstract il livello di accesso ai dati e deridere i risultati provenienti dal database.Sembra che i test sono piuttosto costosi e si dovrebbe fare tutto il vostro logica di query sul database e la logica di business in assembly, che non si cura davvero quali sono i risultati che vengono restituiti.

Potrai anche essere in grado di testare la vostra gestione delle eccezioni molto meglio.

Un'altra opzione è quella di avere una funzione speciale che genererà il vostro eccezioni, che consente di impostare un interruttore in testfixture che dice che un'eccezione si è verificato.

public abstract class CleanOnErrorFixture
{
     protected bool threwException = false;

     protected void ThrowException(Exception someException)
     {
         threwException = true;
         throw someException;
     }

     protected bool HasTestFailed()
     {
          if(threwException)
          {
               threwException = false; //So that this is reset after each teardown
               return true;
          }
          return false;
     }
}

Quindi, usando il tuo esempio:

[TestFixture]
public class SomeFixture : CleanOnErrorFixture
{
    [Test]
    public void MyFailTest()
    {
        ThrowException(new InvalidOperationException());
    }

    [Test]
    public void MySuccessTest()
    {
        Assert.That(true, Is.True);
    }

    [TearDown]
    public void CleanUpOnError()
    {
        if (HasLastTestFailed()) CleanUpDatabase();
    }
}

L'unico problema qui è che la traccia dello Stack porterà alla CleanOnErrorFixture

Una possibilità che non è stato detto finora è quello di avvolgere la prova in oggetto TransactionScope, in modo che non importa cosa succede, come il test non commette mai nulla per il DB.

Ecco alcuni dettagli sulla tecnica.Probabilmente si può trovare di più se fate una ricerca su i test di unità e transactionscope (anche se si sta realmente facendo il test di integrazione, se si ha colpito un DB).Io l'ho usato con successo in passato.

Questo approccio è semplice, non richiede la pulizia e assicura che i test sono isolati.

Edit - ho appena notato Ray Hayes risposta è simile al mio.

Come fa a fallire?E ' possibile mettere in una prova (prove) / catch (fix rotto db) / blocco finally?

Oppure si potrebbe chiamare un metodo privato di correzione, quando hai controllato il tuo fail condizione.

Non sto dicendo che questa è una grande idea, ma dovrebbe funzionare.Ricordati che l'asserzione errori sono solo eccezioni.Inoltre, non dimenticate che c'è anche un [TestFixtureTearDown] attributo che viene eseguito solo una volta, dopo tutti i test l'apparecchio ha eseguito.

Utilizzando questi due fatti si può scrivere qualcosa di simile impostazione di una bandiera, se un test fallito e controllare il valore del flag nel dispositivo della prova di strappo verso il basso.

Io non consiglio questo, ma che avrebbe funzionato.Non si è fatto uso di NUnit come previsto, ma si può fare.


[TestFixture]
public class Tests {
     private bool testsFailed = false;

     [Test]
     public void ATest() {
         try {
             DoSomething();
             Assert.AreEqual(....);
         } catch {
            testFailed = true;
         }
     }

     [TestFixtureTearDown]
     public void CleanUp() {
          if (testsFailed) {
              DoCleanup();
          }
     }
}

È possibile aggiungere un [TearDown] con il metodo di
if (TestContext.CurrentContext.Result.Status != TestStatus.Passed)
un po ' di codice da eseguire se il test è fallito.

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