Domanda

Sto usando C # 3.0 e NUnit. Mi chiedo se esiste un modo standard per eseguire unit test sul codice che viene eseguito dopo un certo periodo di tempo. Ad esempio, ho una semplice classe statica con cui posso registrare i metodi e farli chiamare n millisecondi più tardi. Devo assicurare che venga chiamato il codice nei metodi delegati.

Ad esempio, il seguente test passerà sempre, perché non ci sono asserzioni fatte fino a quando il metodo non è uscito.

[Test]
public void SampleTest()
{
    IntervalManager.SetTimeout(delegate{ 
        Assert.Equals(now.Millisecond + 100, DateTime.Now.Millisecond); 
    }, 100);
}

È anche possibile unità di codice di test che non viene eseguito immediatamente?

Saluti,

Paul

È stato utile?

Soluzione

Che ne dici di questo? Fa sì che il test si blocchi per un certo tempo massimo previsto affinché il callback venga attivato e completato prima del salvataggio, segnalando un errore.

public void Foo() {
    AutoResetEvent evt = new AutoResetEvent(false);
    Timer t = new Timer(state => {
        // Do work
        evt.Set();
    }, null, 100, Timeout.Infinite);
    if (evt.WaitOne(500)) {
        // method called and completed
    } else {
        // timed out waiting
    }
}

Altri suggerimenti

Quindi cosa stai testando esattamente? Stai testando che i timer funzionano? O che il tuo codice imposta correttamente un timer in modo che alla scadenza il timer esegua una richiamata? Senza sapere che aspetto ha il codice, presumo che ciò che vuoi veramente testare sia quest'ultimo. La mia risposta sarebbe che (1) questo sarà probabilmente difficile con i metodi statici e (2) probabilmente dovrai usare l'iniezione di dipendenza e iniettare i timer finti, ecc. Che in realtà non eseguono il metodo risultante ma registrano in base alle aspettative che le chiamate giuste siano state effettuate dal tuo codice.

Come nota a margine. Di solito proviamo a contrassegnare i nostri test a bassa velocità con una categoria NUnit e può scegliere di saltare quei test su alcune build.

Sì, farlo come nel tuo esempio non funzionerà.

Piuttosto, ti consiglio di creare una classe di test, da utilizzare come delegato, che registri quando è stato chiamato il suo metodo e a che ora.

Inietti quindi il tuo finto in IntervalManager che vuoi testare. Il tuo metodo di test deve quindi attendere IntervalManager (utilizzando un metodo adatto fornito da IntervalManager o attendere solo qualche secondo), quindi puoi verificare lo stato della classe di test.

A proposito, questo approccio è di solito definito beffardo ; in questo caso la classe test sarebbe l'oggetto simulato.

Come indicato da un paio di altre risposte, i test di lunga durata sono generalmente una cattiva idea. Per facilitare il test di questo componente dovresti considerare che hai davvero due cose distinte che stai provando a testare.

  1. Quando si registra un'esecuzione delegata a tempo, viene impostato l'orario corretto. Questo probabilmente deve essere testato con varie combinazioni di timeout e numero di delegati.
  2. Il delegato viene eseguito nel modo corretto.

Separare i test in questo modo ti consentirebbe di verificare che il tuo meccanismo di temporizzazione funzioni come previsto con un numero limitato di timeout brevi (test di tutti i casi che devi considerare). Ricorda che probabilmente avrai bisogno di un po 'di margine di manovra nel tempo effettivo necessario per eseguire un determinato delegato in base al carico corrente sul sistema e alla complessità del tuo codice componente (ovvero in IntervalManager ).

Ovviamente puoi provarlo. Devi solo aspettare che venga eseguito.

Forse mi manca qualcosa, ma il test di unità di Visual Studio ha attributi speciali che puoi mettere su metodi per controllare l'ordine di esecuzione e altre cose. Questo avrebbe dovuto essere generato automaticamente quando hai realizzato il progetto di unit testing:

    #region Additional test attributes
    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    //[ClassInitialize]
    //public static void MyClassInitialize(TestContext testContext) {
    //}
    //
    //Use ClassCleanup to run code after all tests in a class have run
    //[ClassCleanup()]
    //public static void MyClassCleanup()
    //{
    //}
    //
    //Use TestInitialize to run code before running each test
    //[TestInitialize()]
    //public void MyTestInitialize()
    //{
    //}
    //
    //Use TestCleanup to run code after each test has run
    //[TestCleanup()]
    //public void MyTestCleanup()
    //{
    //}
    //
    #endregion

Quindi l'uso di [ClassInitialize] dovrebbe consentire di scrivere tutto ciò che deve essere eseguito per primo in un metodo. Quindi i test possono essere eseguiti. Oppure puoi usare [TestInitialize] per eseguire il codice prima di ogni test.

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