Struttura di test unitaria incentrata sulle responsabilità

1

Non ho intenzione di chiedere quale sia la migliore struttura di test dell'unità o come farlo di nuovo. Ci sono già abbastanza domande a riguardo. Invece vorrei che tu mi dicessi cos'altro potrei prendere in considerazione nella struttura che mi è venuta in mente (prendendo in prestito da altre idee e aggiungendone di nuove).

Ho pensato a come migliorare la mia struttura di test perché dopo aver scritto alcuni test stava diventando sempre più difficile vedere quali casi ho già trattato e quali sono ancora mancanti. Un lungo elenco di metodi di test non mostrava realmente quali caratteristiche stessero effettivamente testando. Stavano testando alcuni casi ma che tipo di casi? Sono per lo più troppo aspecifici per me. Volevo che fossero una documentazione di ciò che è richiesto per una classe / metodo per funzionare e come funziona piuttosto che cosa fa se non lo fa o se funziona.

Quindi ho capovolto la logica e invece di scrivere cosa succede realmente scrivo ciò che la classe / metodo deve riconoscere come uso errato o corretto.

Per implementarlo ho aggiunto due nuovi layer ai test dei test prerequisiti e test di utilizzo della struttura di test. Questo mi consente di tenere a mente ciò che ho già testato e ciò che manca. Proviamo sul seguente esempio (semplificato):

public class Configuration
{
     public static XDocument LoadConfiguration(string fileName)
     {
         if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName");

         if (!File.Exists(fileName)) throw new FileNotFoundException(fileName);

         return XDocument.Load(fileName);
     }
}

Per testarlo, prima definisco (se necessario) alcuni test per testare i prerequisiti che devono essere soddisfatti prima che sia persino possibile lavorare con una classe o un metodo.

Poi provo i diversi possibili usi della classe o del metodo.

[TestClass]
public class ConfigurationTests
{
    [TestClass]
    public class LoadConfiguration_TestPrerequisites
    {
        [TestMethod]
        public void FileNameMustNotBeNull()
        {
            // assert that ArgumentNullException is thrown when fileName is null
        }

        [TestMethod]
        public void FileNameMustExist()
        {
            // assert that FileNotFoundException is thrown when file doesn't exist
        }
    }

    [TestClass]
    public class LoadConfiguration_TestUsage
    {
        [TestMethod]
        public void MustImportValidXml()
        {
            // assert that configuration was loaded
        }

        [TestMethod]
        public void MustNotImportInvalidXml()
        {
            // assert that some other exception was thrown on invalid xml
        }
    }
}

La mia domanda è: quali altre responsabilità chiave potrei mancare qui oltre a prerequisiti e utilizzo? In qualche modo non sono ancora del tutto soddisfatto e il mio istinto mi dice che ho trascurato qualcosa.

Il mio obiettivo è anche quello di descrivere (nominare) i test in modo più naturale, in modo che mostrino le responsabilità della classe / metodo e non ciò che accade. Trovo più semplice capire FileNameMustNotBeNull di Throws_FileNotFoundException . perché l'implementazione potrebbe cambiare (potrei decidere di restituire un valore nullo se il nome del file era nullo) ma non può ancora essere nullo.

    
posta t3chb0t 07.11.2015 - 12:49
fonte

1 risposta

1

Ecco alcune ampie categorie di test che potrebbero essere ciò di cui hai bisogno:

  • gestione dell'input non valido (ciò che chiami prerequisiti )
  • correttezza della logica del percorso felice (ciò che chiami uso ) - questa può essere abbastanza grande da valere la pena di decomporsi ulteriormente, ma è specifica del caso

  • gestione dei casi limite (in cui l'input si trova ai margini dell'intervallo valido)

  • gestione di casi negativi (verifica che qualcosa di indesiderato non accada)

Tuttavia non li chiamerei responsabilità. Un modulo dovrebbe avere una sola responsabilità (è funzione).

La convalida dei prerequisiti non è una responsabilità "secondaria" di un modulo; è solo un modo per garantire che il modulo sia in grado di elaborare l'input (eseguirne la funzione) correttamente.

    
risposta data 07.11.2015 - 16:07
fonte

Leggi altre domande sui tag