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.