Metti test unitari nello stesso progetto o in un altro progetto?


137

Metti i test unitari nello stesso progetto per comodità o li metti in un assieme separato?

Se li mettete in un assemblaggio separato come facciamo noi, finiremo con un numero di progetti extra nella soluzione. È ottimo per i test unitari durante la codifica, ma come si fa a rilasciare l'applicazione senza tutti questi assemblaggi extra?

Risposte:


100

A mio avviso, i test unitari dovrebbero essere collocati in un assieme separato dal codice di produzione. Di seguito sono riportati alcuni svantaggi del posizionamento di test unitari nello stesso assieme o assiemi del codice di produzione:

  1. I test unitari vengono spediti con il codice di produzione. L'unica cosa fornita con il codice prodotto è il codice di produzione.
  2. Le assemblee saranno inutilmente gonfiate dai test unitari.
  3. I test unitari possono influenzare i processi di compilazione come la compilazione automatica o continua.

Non conosco davvero nessun professionista. Avere un progetto extra (o 10) non è un contro.

Modifica: maggiori informazioni su costruzione e spedizione

Raccomanderei inoltre che qualsiasi processo di costruzione automatizzato collochi la produzione e i test unitari in luoghi diversi. Idealmente, il processo di generazione del test di unità viene eseguito solo se il codice di produzione viene compilato e copia i file del prodotto nella directory dei test di unità. In questo modo, i bit effettivi vengono separati per la spedizione, ecc. Inoltre, è abbastanza banale eseguire test di unità automatizzati a questo punto su tutti i test in una determinata directory.

Riassumendo, ecco l'idea generale per una compilazione giornaliera, test e spedizione di bit e altri file:

  1. Viene eseguito il build di produzione, posizionando i file di produzione in una specifica directory di "produzione".
    1. Costruisci solo progetti di produzione.
    2. Copia i bit compilati e altri file in una directory "produzione".
    3. Copiare bit e altri file in una directory candidata al rilascio, ovvero una directory del rilascio natalizio sarebbe "Release20081225".
  2. Se la build di produzione ha esito positivo, viene eseguita la build di unit test.
    1. Copia il codice di produzione nella directory "test".
    2. Costruire unit test nella directory "test".
    3. Eseguire test unitari.
  3. Invia notifiche di build e risultati dei test unitari agli sviluppatori.
  4. Quando viene accettato un candidato di rilascio (come Release20081225), spedire questi bit.

15
IMO i contro che elenchi non sono sempre applicabili. Un professionista per lo stesso progetto è il raggruppamento più semplice di test testati di classe +: questi piccoli vantaggi fanno molto per scrivere test. Qui vince la preferenza personale, e talvolta i tuoi punti sono rilevanti, ma non sempre.
orip,

7
Dovresti prima chiedere se è necessario rimuovere i test durante la spedizione. Se sì, hai bisogno di un progetto separato. In caso contrario, utilizzare gli altri pro e contro per decidere. Le persone che presumono di non poter distribuire i test raggiungeranno sempre la conclusione "progetto separato" per impostazione predefinita.
orip

7
Non vedo alcun vantaggio nella spedizione di codice non di produzione come i test unitari, e ci sono molti aspetti negativi. I test delle unità di spedizione indicano che hai a che fare con più bit che devono essere distribuiti. I test unitari hanno anche un insieme separato di dipendenze. Ora stai spedendo NUnit, Rhino o Moq, ecc. È ancora più gonfio. Inserire i test unitari in un progetto separato richiede solo un piccolo sforzo, e questo è un costo una tantum. Sono molto a mio agio con la conclusione che i test unitari non dovrebbero essere spediti.
Jason Jackson,

4
In alternativa all'approccio del "progetto separato" per la rimozione di unit test nel codice di produzione, prendere in considerazione l'uso di un simbolo personalizzato e direttive del compilatore come #if. Questo simbolo personalizzato può essere attivato utilizzando i parametri della riga di comando del compilatore negli script del software CI. Vedi msdn.microsoft.com/en-us/library/4y6tbswk.aspx .
Rich C

23
.NET è dietro la curva per avere test in un progetto completamente separato, e scommetterei che questo cambierà presto. Non c'è motivo per cui il processo di compilazione non possa essere fatto per ignorare il codice di test nella build di rilascio. Ho usato diversi generatori di app Web che fanno questo. Penso che sia assolutamente meglio includere i file di codice di unit test insieme ai file di codice che descrivono, suddivisi nella stessa gerarchia di file. Google lo consiglia, chiamato organizzazione dei file "frattali". Perché i test sono diversi dai commenti in linea e dalla documentazione del readme? Il compilatore ignora volentieri quest'ultimo.
Brandon Arnold,

112

Progetto separato, ma nella stessa soluzione. (Ho lavorato su prodotti con soluzioni separate per i test e il codice di produzione - è orribile. Passi sempre tra i due.)

Le ragioni di progetti separati sono come affermato da altri. Se si utilizzano test basati sui dati, è possibile che si verifichi una notevole quantità di gonfiore se si includono i test nell'assieme di produzione.

Se è necessario accedere ai membri interni del codice di produzione, utilizzare InternalsVisibleTo .


+1: Ho appena incontrato unit test nello stesso progetto del codice principale ed è difficile trovare i test tra il codice reale, anche se è stata seguita una convenzione di denominazione.
Fenton,

32
Per i lettori meno esperti ciò significa che si aggiunge [assembly:InternalsVisibleTo("UnitTestProjectName")]al AssemblyInfo.csfile di progetto .
Margus,

Aggiunta molto utile Margus. Grazie a Jon per aver menzionato InternalsVisibleTo in questo contesto.
Bjørn Otto Vasbotten,

1
@jropella: se stai firmando l'assembly prod, puoi comunque rendere visibili solo gli interni agli assembly firmati. E ovviamente se stai eseguendo un codice in piena fiducia, hanno accesso tramite la riflessione ...
Jon Skeet,

2
@jropella: La riflessione non si preoccupa di InternalsVisibleTo comunque ... ma non avresti visto un assembly firmato fidarsi di un assembly senza segno utilizzando InternalsVisibleTo, perché ciò è impedito in fase di compilazione.
Jon Skeet,

70

Non capisco la frequente obiezione alla distribuzione di test con codice di produzione. Ho guidato una squadra in un piccolo microcap (cresciuto da 14 a 130 persone). Avevamo una mezza dozzina di app Java e abbiamo trovato ESTREMAMENTE prezioso per distribuire test sul campo per eseguirli su uno specificomacchina che mostrava comportamenti insoliti. Si verificano problemi casuali sul campo e riuscire a lanciare qualche migliaio di test unitari al mistero a costo zero è stato inestimabile e spesso sono stati diagnosticati problemi in pochi minuti ... inclusi problemi di installazione, problemi di RAM instabili, problemi specifici della macchina, problemi di rete instabili, ecc. ecc. Penso che sia incredibilmente prezioso mettere le prove in campo. Inoltre, si verificano problemi casuali in momenti casuali ed è bello avere i test unitari già seduti lì in attesa di essere eseguiti in un momento. Lo spazio sul disco rigido è economico. Proprio come proviamo a tenere insieme dati e funzioni (progettazione OO), penso che ci sia qualcosa di fondamentalmente prezioso nel mantenere insieme codice e test (funzione + test che convalidano le funzioni).

Vorrei mettere i miei test nello stesso progetto in C # /. NET / Visual Studio 2008, ma non ho ancora studiato abbastanza per raggiungerlo.

Un grande vantaggio di mantenere Foo.cs nello stesso progetto di FooTest.cs è che agli sviluppatori viene costantemente ricordato quando a una classe manca un test di pari livello! Questo incoraggia migliori pratiche di codifica test-driven ... i buchi sono più evidenti.


17
Posso vedere quanto sarebbe utile con i test di integrazione, ma per i test unitari? Se le scrivi correttamente, non dovrebbero avere dipendenze dalla macchina.
Joel McBeth,

+1 Sono d'accordo. Come sto facendo principalmente .NET, i test di spedizione con codice di produzione hanno anche il piacevole effetto collaterale di ridurre la fase di integrazione continua di compilazione e test: 1) Solo metà dei progetti da costruire, 2) tutti i gruppi di test al posto dell'applicazione principale quindi è più facile parametrizzare il test runner. Quando si utilizza Maven questo argomento non regge, ovviamente. Infine, i miei clienti sono più tecnici e amano davvero poter eseguire i test da soli poiché questo sta documentando lo stato corrente rispetto alle specifiche.
mkoertgen,

Penso che possa avere perfettamente senso fare test di integrazione in stile TDD / BDD, ovvero scrivere un codice di test che può essere facilmente automatizzato con test runner standard come NUnit, xUnit ecc. Naturalmente, non si deve confondere l'unità con i test di integrazione. Assicurati solo di sapere quale serie di test eseguire rapidamente e spesso per la verifica della build (BVT) e quali per i test di integrazione.
mkoertgen,

1
Il motivo per cui le persone si oppongono è perché non è quello a cui sono abituati. Se la loro prima esperienza test di unità fosse che erano all'interno del codice di produzione, in effetti, con una sintassi specifica nel linguaggio di programmazione che indicava quale test è associato a un metodo di produzione, avrebbero giurato che quello era un aspetto inestimabile del flusso di lavoro di sviluppo ed è assolutamente folle metterli in un progetto separato. La maggior parte degli sviluppatori è così. Seguaci.
zumalifeguard,

19

Inserisci i test unitari nello stesso progetto del codice per ottenere una migliore incapsulamento.

Puoi facilmente testare metodi interni, il che significa che non renderai pubblici i metodi che avrebbero dovuto essere interni.

Inoltre è davvero bello avere i test unitari vicini al codice che stai scrivendo. Quando scrivi un metodo puoi facilmente trovare i test unitari corrispondenti perché è nello stesso progetto. Quando si crea un assembly che include unitTest, tutti gli errori in unitTest ti daranno un errore di compilazione, quindi è necessario mantenere aggiornato il più semplice, solo per costruire. Avere unittest in un progetto separato, potrebbe far dimenticare ad alcuni sviluppatori la costruzione del progetto unittest e la mancanza dei test falliti per un po '.

E puoi rimuovere i test unitari dal codice di produzione, usando i tag di compilazione (IF #Debug).

I test di integrazione automatica (realizzati in NUnit) dovrebbero essere in un progetto separato poiché non appartengono a un singolo progetto.


1
Ciò significa che non è possibile eseguire test sulla Releasebuild e pochi "heisenbugs" possono cadere.
hIpPy

3
Con gli assembly di amici ( msdn.microsoft.com/en-us/library/0tke9fxk.aspx ) l'utilizzo InternalsVisibleToconsente di testare metodi interni da un progetto di test separato
ParoX

3
@hlpPy: puoi configurare simboli di compilazione condizionale arbitraria e puoi avere più di 2 configurazioni di build. Per esempio; potresti avere Debug, Approvale Release; e compilare l'approvazione con tutte le ottimizzazioni di rilascio. Inoltre, in genere i test unitari non sono ottimi per rilevare gli heisenbugs in primo luogo (secondo la mia esperienza). Tendi ad avere bisogno di specifici test di regressione dell'integrazione per quelli - e potresti benissimo metterli fianco a fianco.
Eamon Nerbonne,

Un progetto separato dà lo stesso errore di tempo di compilazione. Se hai bisogno di approfondire il codice, suona più come se avessi bisogno di più astrazioni di hackerare i test unitari nel tuo codice. Anche fare affidamento su condizionali nel tuo codice per cambiare ramo non va bene. Sembra più che i test Unit e Integrations vengano confusi.
Nick Turner,

12

Dopo aver trascorso un po 'di tempo in progetti TypeScript, dove i test sono spesso inseriti in un file accanto al codice che stanno testando, sono cresciuto preferendo questo approccio piuttosto che tenerli separati:

  • È più veloce passare al file di prova.
  • È più facile ricordare di rinominare i test quando si rinomina la classe da testare.
  • È più facile ricordare di spostare i test quando si sposta la classe da testare.
  • È immediatamente ovvio se in una classe mancano i test.
  • Non è necessario gestire due strutture di file duplicate, una per i test e una per il codice.

Quindi, quando ho iniziato di recente un nuovo progetto .NET Core, volevo vedere se era possibile imitare questa struttura in un progetto C # senza spedire i test o assemblare i test con la versione finale.

Inserire le seguenti righe nel file di progetto sembra funzionare bene finora:

  <ItemGroup Condition="'$(Configuration)' == 'Release'">
    <Compile Remove="**\*.Tests.cs" />
  </ItemGroup>
  <ItemGroup Condition="'$(Configuration)' != 'Release'">
    <PackageReference Include="nunit" Version="3.11.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
  </ItemGroup>

Quanto sopra assicura che nella Releaseconfigurazione tutti i file nominati *.Tests.cssiano esclusi dalla compilazione e che vengano rimossi i riferimenti al pacchetto di unit testing richiesti.

Se vuoi ancora essere in grado di testare le classi nella loro configurazione di rilascio, puoi semplicemente creare una nuova configurazione derivata da Releasequalcosa di simile ReleaseContainingTests.


Aggiornamento: dopo aver usato questa tecnica per un po 'ho anche trovato utile personalizzare le tue icone in VS Code per far risaltare un po' di più i test (e altre cose) nel riquadro Explorer:

Screenshot del codice VS.

Per fare ciò, usa l'estensione Tema icona materiale e aggiungi qualcosa come il seguente alle tue preferenze VS Code JSON:

"material-icon-theme.files.associations": {
  "*.Tests.cs": "test-jsx",
  "*.Mocks.cs": "merlin",
  "*.Interface.cs": "yaml",
}

Esattamente quello che abbiamo appena iniziato a fare
Matthew Steeples

Hai funzionato anche per le librerie di classi standard .net?
Zenuka,

10

I miei test unitari vanno sempre in un progetto separato. In effetti, per ogni progetto che ho nella mia soluzione, esiste un progetto di test separato che lo accompagna. Il codice di test non è il codice dell'applicazione e non deve essere mescolato con esso. Un vantaggio nel mantenerli in progetti separati - almeno usando TestDriven.Net - è che posso fare clic con il tasto destro su un progetto di test ed eseguire tutti i test in quel progetto, testando un'intera libreria di codice dell'applicazione con un clic.


1
Quindi come testate le classi interne? O metti alla prova solo le classi pubbliche?
user19371

7
<assembly: InternalsVisibleTo = "TestProject" /> se necessario, anche se in genere collaudo solo interfacce pubbliche.
tvanfosson,

@tvanfosson, cosa fai con le specifiche (Specflow)? Avresti un progetto separato o li inseriresti nel progetto Unit Test? +1.
w0051977,

@ w0051977 Non ho mai usato Specflow, quindi non lo so
tvanfosson,

@tvanfosson, che dire dei test di integrazione? Li metteresti in un progetto separato per i test unitari?
w0051977,

10

Se viene utilizzato il framework NUnit , c'è un motivo in più per inserire i test nello stesso progetto. Si consideri il seguente esempio di codice di produzione misto a test unitari:

public static class Ext
{
     [TestCase(1.1, Result = 1)]
     [TestCase(0.9, Result = 1)]
     public static int ToRoundedInt(this double d)
     {
         return (int) Math.Round(d);
     }
}

I test unitari qui servono come documentazione e specifiche per il codice da testare. Non so come ottenere questo effetto di auto-documentazione, con i test situati in un progetto separato. L'utente della funzione dovrebbe cercare i test per vedere quei casi di test, il che è improbabile.

Aggiornamento : so che tale utilizzo TestCasedell'attributo non era intenzionato dagli sviluppatori di NUnit, ma perché no?


2
Mi piace questa idea; con alcuni esempi di input e output attesi proprio con il codice.
Preston McCormick,

3

Fluttuo tra lo stesso progetto e progetti diversi.

Se stai rilasciando una libreria rilasciando il codice di test con il codice di produzione è un problema, altrimenti trovo che non lo sia (anche se c'è una forte barriera psicologica prima di provare).

Quando inserisco i test nello stesso progetto, trovo più facile passare da un test all'altro e al codice che testano, e più facilmente è possibile effettuare il refactoring / spostarli.


3

Li ho messi in progetti separati. Il nome dell'assemblaggio rispecchia quello degli spazi dei nomi, come regola generale per noi. Quindi, se esiste un progetto chiamato Company.Product.Feature.sln, ha un output (nome dell'assembly) di Company.Product.Feature.dll. Il progetto di test è Company.Product.Feature.Tests.sln, cedendo Company.Product.Feature.Tests.dll.

È meglio tenerli in un'unica soluzione e controllare l'output tramite Configuration Manager. Abbiamo una configurazione denominata per ciascuno dei rami principali (sviluppo, integrazione, produzione) al posto dell'utilizzo del debug e della versione predefiniti. Dopo aver configurato le configurazioni, è possibile includerle o escluderle facendo clic sulla casella di controllo "Crea" in Configuration Manager. (Per ottenere Configuration Manager, fare clic con il pulsante destro del mouse sulla soluzione e andare a Configuration Manager.) Nota, a volte trovo che il CM in Visual Studio sia difettoso. Un paio di volte, ho dovuto andare nel progetto e / o nei file della soluzione per ripulire gli obiettivi che ha creato.

Inoltre, se si utilizza Team Build (e sono sicuro che gli altri strumenti di build .NET siano gli stessi) è quindi possibile associare la build a una configurazione denominata. Ciò significa che se non si creano i test unitari per la build "Produzione", ad esempio, anche il progetto di build può essere a conoscenza di questa impostazione e non crearli poiché sono stati contrassegnati come tali.

Inoltre, abbiamo usato XCopy per abbandonare la build machine. Lo script avrebbe semplicemente omesso di copiare qualsiasi cosa denominata * .Tests.Dll dalla distribuzione. Era semplice, ma ha funzionato.


1

Direi di tenerli separati.

Oltre agli altri motivi citati, avere codice e test insieme distorce i numeri di copertura dei test. Quando si riferisce sulla copertura dei test unitari, la copertura segnalata è maggiore perché i test sono coperti quando si eseguono test unitari. Quando si segnala la copertura del test di integrazione, la copertura riportata è inferiore poiché i test di integrazione non eseguono test unitari.


Ciò non dipende dalla tecnologia utilizzata per coprire i test? Voglio dire, OpenCover considera le righe di codice coperte se vengono eseguite da un test, comprese le righe del test stesso in modo che se i test hanno eccezioni impreviste, vengono contrassegnati anche come scoperti.
Isaac Llopis,

0

Sono davvero ispirato dal framework di unit test della libreria Flood NN di Robert Lopez. Utilizza un progetto diverso per ogni singola classe testata di unità e ha una soluzione che contiene tutti questi progetti, nonché un progetto principale che compila ed esegue tutti i test.

La cosa bella è anche il layout del progetto. I file di origine si trovano in una cartella, ma la cartella per il progetto VS è in basso. Ciò consente di creare diverse sottocartelle per diversi compilatori. Tutti i progetti VS vengono spediti con il codice, quindi è molto facile per chiunque eseguire uno o tutti i test unitari.


0

So che questa è una domanda molto antica, ma vorrei aggiungere la mia esperienza lì di recente ho cambiato l'abitudine di test unitari da progetti separati alla stessa.

Perché?

In primo luogo, tendo a mantenere la stessa struttura delle cartelle del progetto principale con il progetto di test. Quindi, se ho un file sottoProviders > DataProvider > SqlDataProvider.cs sto creando la stessa struttura nei miei progetti di unit test comeProviders > DataProvider > SqlDataProvider.Tests.cs

Ma dopo che il progetto sta diventando sempre più grande, una volta spostati i file da una cartella all'altra o da un progetto a un altro, allora sta diventando un lavoro molto ingombrante per sincronizzare quelli con i progetti di unit test.

In secondo luogo, non è sempre molto facile spostarsi dalla classe da testare alla classe di unit test. Questo è ancora più difficile per JavaScript e Python.

Di recente, ho iniziato a esercitarmi in questo, ogni singolo file che ho creato (ad esempio SqlDataProvider.cs) sto creando un altro file con suffisso Test, comeSqlDataProvider.Tests.cs

All'inizio sembra che si gonfino i file e i riferimenti alle librerie, ma a lungo termine, eliminerai la sindrome dei file in movimento a prima vista, e inoltre ti assicurerai che ogni singolo file che sono candidati al test avrà un file di coppia con .Tests suffisso. Ti dà la possibilità di saltare nel file di test (perché è fianco a fianco) invece di guardare attraverso un progetto separato.

È anche possibile scrivere regole aziendali per eseguire la scansione del progetto e identificare la classe che non ha file .Test e segnalarli al proprietario. Inoltre puoi dire facilmente al tuo runner di test le .Testsclassi target .

Soprattutto per Js e Python non sarà necessario importare i riferimenti da percorsi diversi, è possibile semplicemente utilizzare lo stesso percorso del file di destinazione testato.

Sto usando questa pratica da un po 'di tempo e penso che sia un compromesso molto ragionevole tra dimensione del progetto vs manutenibilità e curva di apprendimento per i nuovi arrivati ​​al progetto.


Non sono d'accordo. È molto più organizzato per raggruppare gli articoli e poi testare le singole classi. Se stai testando i tuoi fornitori; Avresti un IProvider, ISqlProvider e un MockSqlConnection. In una cartella del provider si avrebbe il test unitario per ciascun provider. Puoi approfondire i test unitari, ma poi basta scrivere test e non codice e potresti anche rimuovere il pro e iniziare a scrivere codice per testare.
Nick Turner,

0

Come altri hanno risposto, inserire i test in progetti separati

Una cosa che non è stata menzionata è il fatto che non si può effettivamente correre nunit3-console.execontro altro che .dllfile.

Se hai intenzione di eseguire i test tramite TeamCity, ciò rappresenterà un problema.

Supponiamo che tu abbia un progetto di applicazione console. Durante la compilazione, restituisce un eseguibile .exenella bincartella.

Dal nunit3-console.exenon saresti in grado di eseguire alcun test definito nell'applicazione console.

In altre parole, un'applicazione console restituisce un exefile e una libreria di classi restituisce dllfile.

Sono stato morso da questo oggi e fa schifo :(


0

Recentemente ho implementato nella finestra mobile. Non vedo il valore nell'avere un progetto separato quando Dockerfile può facilmente copiare la directory / src e lasciare la directory / test. Ma sono nuovo di Dotnet e potrebbe mancare qualcosa.


-2

Progetti separati, anche se discuto con me stesso se dovrebbero condividere lo stesso svn. Al momento, sto dando loro repository svn separati, uno chiamato

"MyProject" - per il progetto stesso

e uno ha chiamato

"MyProjectTests" - per i test associati a MyProject.

Questo è abbastanza pulito e ha il vantaggio che si impegna nel progetto e che i test sono abbastanza separati. Significa anche che puoi consegnare lo svn del progetto se necessario, senza dover rilasciare i test. Significa anche che puoi avere directory branch / trunk / tag per i tuoi test e per il tuo progetto.

Ma sono sempre più propenso ad avere qualcosa di simile al seguente, in un unico repository svn, per ogni progetto.

Il mio progetto
| \ Trunk
| | \ Codice
| \ Test
| \ Tag
| | \ 0.1
| | | \ Codice
| | \ Test
| \ 0.2
| | \ Codice
| \ Test
\ Branches
  \ MyFork
   | \ Codice
    \Test

Sarei interessato a sapere cosa pensano gli altri di questa soluzione.


5
Non dovresti aver bisogno di commit separati per test e codice app. Dovrebbero andare di pari passo e gli impegni dovrebbero coinvolgere entrambi, specialmente se si utilizza il design in stile * DD.
Eddiegroves
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.