2011-10-14 9 views
7

Ecco il problema di progettazione del test dell'unità C#:Problema di progettazione dell'unità C#: come ridurre la ridondanza nella scrittura del test unitario?

Ho un test che voglio eseguire su un codice che modifica un file. Il mio test eseguirà il codice che modifica il file, quindi controlla il risultato. Piuttosto diretto finora ...

Il problema è che ho circa 10 file (che presto saranno molti di più) su cui voglio eseguire lo stesso test dell'unità. Non voglio scrivere un nuovo test di unità per ogni file, quando il test stesso è davvero lo stesso.

Potrei scrivere un singolo test che interroga i file nella cartella e quindi esegue la logica di test su ciascun file, ma l'utilizzo di questo metodo segnalerebbe solo un risultato pass/fail per l'intero set di file.

Id mi piace creare una sorta di sistema dinamico in cui ogni file che inserisco in una particolare cartella viene eseguito nonostante questo stesso test di unità. Quindi, se nella cartella sono presenti 25 file, il test viene eseguito 25 volte e le reult del test unità riportano che sono stati eseguiti 25 test e include pass/fail separati per ciascuno.

Qualche idea come o se questo può essere fatto in un test di unità C#? O con un test di Nunit?

Grazie! E spero che questa non sia una domanda doppia. Mi sono guardato intorno, ma non ho trovato nulla.

+1

Alcuni sostengono che un test che coinvolge IO (manipolando il file system in questo caso) non è un test unitario. Un approccio alternativo consiste nel rappresentare le operazioni di I/O come dipendenze iniettabili. Tuttavia, la maggior parte dei tipi di System.IO (e certamente la statica) non si prestano a questo. Un approccio che ho usato è quello di creare assiemi proxy di tipi di inoltro di metodo banali e interfacce corrispondenti. Questo ti permette di lasciare questo strato proxy sottile non testato e il tuo livello logico completamente _unit_ testato. Microsoft Moles lo fa in modo dinamico ma è ancora un WIP. –

risposta

3

Un'altra opzione è quella di utilizzare i modelli T4 per generare dinamicamente i test per voi in base al numero di file nella directory. Aggiungi questo file ".tt" al tuo progetto di test unitario.

Ora ogni volta che esegui una build che dovrebbe accadere prima di premere il pulsante "Esegui tutti i test in soluzione" in Visual Studio, dovrebbe generare test unitari per tutti i file nella directory con il nome del file nel test dell'unità . Quindi la tua esecuzione di test dovrebbe avere una bella lista di tutti i file testati e dei loro stati.

<#@ template debug="false" hostspecific="true" language="C#v3.5" #> 
<#@ assembly name="System.Core.dll" #> 
<#@ assembly name="System.Data.dll" #> 
<#@ import namespace="System.IO" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#@ import namespace="System.Linq" #> 
<#@ output extension=".cs" #> 
<# 
      string inputDirectory = @"d:\temp"; 
      var files = System.IO.Directory.GetFiles(inputDirectory); 
#> 
using System; 
using System.Text; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace TestProject1 
{ 
    [TestClass] 
    public class UnitTest1 
    { 
     <# foreach (string filePath in files) { #> 
     [TestMethod] 
     public void TestingFile_<#=System.IO.Path.GetFileNameWithoutExtension(filePath).Replace(' ','_').Replace('-','_')#>() 
     { 
     File currentFile = System.IO.File.Open(@"<#= filePath #>", FileMode.Open); 
     // TODO: Put your standard test code here which will use the file you opened above 
     } 
     <# } #> 
    } 
} 
+1

Questa è roba fantastica! Fino ad ora non conoscevo questa funzione, quindi sono davvero felice di aver finalmente fatto una domanda su StackOverflow! :) – fj40bryan

+1

Grazie a tutti per aver fornito un ottimo input per la mia domanda! – fj40bryan

10

Sembra che quello che ti serve sia un banco di prova parametrizzato: vedi http://www.nunit.org/index.php?p=testCaseSource&r=2.5.

Quindi:

[TestFixture] 
public class Tests 
{ 
    static string[] FileNames = new string[] 
        { "mary.txt", "mungo.txt", "midge.txt" }; 

    [Test, TestCaseSource("FileNames")] 
    public void TestMethod(string fileName) 
    { 
     Assert.That(File.Exists(fileName)); 
    } 
} 

L'attributo [TestCaseSource] dice NUnit per ottenere i valori del parametro stringa dalla matrice di stringhe.

Tale approccio, tuttavia, richiede le costanti statiche per i nomi dei file. Se si preferisce avere un approccio programmatico che legge i file da un database o simili tenta una classe fabbrica in questo modo:

[TestFixture] 
public class Tests 
{ 
    [Test, TestCaseSource(typeof(FilenameFactory), "FileNames")] 
    public bool FileCheck(string fileName) 
    { 
     return File.Exists(fileName); 
    } 
} 

public class FilenameFactory 
{ 
    public static IEnumerable FileNames 
    { 
     get 
     { 
      foreach (var filename in 
        Directory.EnumerateFiles(Environment.CurrentDirectory)) 
      { 
       yield return new TestCaseData(filename).Returns(true); 
      } 
     } 
    } 
} 

La classe TestCaseData genera "test case" che hanno aspettative che possono essere impostate attraverso un fluente interfaccia.

+0

Questa è anche un'ottima soluzione. Grazie Jeremy. – fj40bryan

5

I test parametrizzati (http://www.nunit.org/index.php?p=parameterizedTests&r=2.5) potrebbero essere utili per l'utente in base al modo in cui vengono eseguiti.

Esempio di TestcaseAttribute che è possibile utilizzare l'uso o c'è anche TestCaseSourceAttribute

[TestCase(12,3,4)] 
[TestCase(12,2,6)] 
[TestCase(12,4,3)] 
public void DivideTest(int n, int d, int q) 
{ 
    Assert.AreEqual(q, n/d); 
} 

si ottiene il vantaggio di scrittura e il mantenimento di un test, ma i risultati sono per ciascun caso.

0

Prima di tutto, avere un test dipende da fonti esterne come i file sul disco non è strettamente test di unità (opinione).

Quello che stai cercando è un approccio di test basato sui dati e quindi si dovrebbe cercare strumenti che lo supportano, come MSTest o MBunit. (Segui i collegamenti per maggiori informazioni).

Poi si può fare qualcosa di simile (utilizzando MbUnit):

[TestFixture] 
public class MyTestFixture 
{ 
    [Test] 
    public void WordCounter([TextData(ResourcePath = "Data.txt")] string text) 
    { 
     var wordCounter = new WordCounter(text); 
     int count = wordCounter.Count("Firecrest"); 
     Assert.AreEqual(4, count); // Should not include the plural form "Firecrests". 
    } 
}