Modificatore di accesso "interno" C # quando si eseguono test di unità


469

Sono nuovo nel test unitario e sto cercando di capire se dovrei iniziare a utilizzare più modificatore di accesso "interno". So che se usiamo 'internal' e impostiamo la variabile di assembly 'InternalsVisibleTo', possiamo testare funzioni che non vogliamo dichiarare pubbliche dal progetto di testing. Questo mi fa pensare che dovrei sempre usare 'interno' perché almeno ogni progetto (dovrebbe?) Ha il proprio progetto di test. Ragazzi, potete dirmi un motivo per cui non dovrei farlo? Quando dovrei usare "privato"?


1
Vale la pena ricordare che spesso è possibile evitare la necessità di test unitari dei metodi interni utilizzando System.Diagnostics.Debug.Assert()i metodi stessi.
Mike Marynowski,

Risposte:


1195

Le classi interne devono essere testate e c'è un attributo assembly:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Aggiungi questo al file di informazioni del progetto, ad es Properties\AssemblyInfo.cs.


70
Aggiungilo al progetto in prova (ad es. In Proprietà \ AssemblyInfo.cs). "MyTest" sarebbe il gruppo di test.
EricSchaefer,

86
Questa dovrebbe davvero essere la risposta accettata. Non so voi ragazzi, ma quando i test sono "troppo lontani" dal codice che stanno testando tendo a innervosirmi. Sono tutto per evitare di provare qualcosa contrassegnato come private, ma troppe privatecose potrebbero benissimo indicare una internalclasse che sta lottando per essere estratta. TDD o nessun TDD, preferisco avere più test che testano molto codice, piuttosto che avere pochi test che esercitano la stessa quantità di codice. Evitare di testare le internalcose non aiuta esattamente a ottenere un buon rapporto.
sm

7
C'è una grande discussione in corso tra @DerickBailey e Dan Tao riguardo alla differenza semantica tra interno e privato e la necessità di testare i componenti interni . Vale la pena leggere.
Kris McGinnes,

31
Il wrapping in e #if DEBUG, #endifblock abiliterà questa opzione solo nelle build di debug.
Il vero Edward Cullen il

17
Questa è la risposta corretta Qualsiasi risposta che affermi che solo i metodi pubblici dovrebbero essere testati unitamente manca il punto dei test unitari e fa una scusa. I test funzionali sono orientati alla scatola nera. I test unitari sono orientati alla scatola bianca. Dovrebbero testare "unità" di funzionalità, non solo API pubbliche.
Gunnar,

127

Se vuoi testare metodi privati, dai un'occhiata PrivateObjecte PrivateTypenello Microsoft.VisualStudio.TestTools.UnitTestingspazio dei nomi. Offrono wrapper di facile utilizzo attorno al codice di riflessione necessario.

Documenti: PrivateType , PrivateObject

Per VS2017 e 2019, puoi trovarli scaricando il nuget MSTest.TestFramework


15
In caso di voto negativo, si prega di lasciare un commento. Grazie.
Brian Rasmussen,

35
È stupido votare questa risposta. Indica una nuova soluzione e una davvero buona non menzionata prima.
Ignacio Soler Garcia,

Apparentemente, c'è qualche problema con l'utilizzo di TestFramework per il targeting di app .net2.0 o più recente: github.com/Microsoft/testfx/issues/366
Johnny Wu

41

Aggiungendo alla risposta di Eric, puoi anche configurarlo nel csprojfile:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

O se hai un progetto di test per progetto da testare, potresti fare qualcosa del genere nel tuo Directory.Build.propsfile:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Vedi: https://stackoverflow.com/a/49978185/1678053
Esempio: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12


2
Questa dovrebbe essere la risposta principale imo. Tutte le altre risposte sono molto obsolete poiché .net si sta allontanando dalle informazioni sull'assemblaggio e sta spostando la funzionalità in definizioni csproj.
mBrice1024,

12

Continua a utilizzare privato per impostazione predefinita. Se un membro non deve essere esposto oltre quel tipo, non dovrebbe essere esposto oltre quel tipo, nemmeno all'interno dello stesso progetto. Ciò mantiene le cose più sicure e in ordine: quando si utilizza l'oggetto, è più chiaro quali metodi si intende essere in grado di utilizzare.

Detto questo, penso che sia ragionevole rendere i metodi naturalmente privati ​​interni a fini di test a volte. Preferisco usare la riflessione, che è refactoring-ostile.

Una cosa da considerare potrebbe essere un suffisso "ForTest":

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Quindi quando stai usando la classe all'interno dello stesso progetto, è ovvio (ora e in futuro) che non dovresti davvero usare questo metodo - è lì solo a scopo di test. Questo è un po 'confuso, e non qualcosa che faccio da solo, ma è almeno degno di considerazione.


2
Se il metodo è interno, ciò non preclude il suo utilizzo dal gruppo di prova?
Ralph Shillington,

7
Occasionalmente utilizzo l' ForTestapproccio ma lo trovo sempre brutto (aggiungendo codice che non fornisce alcun valore effettivo in termini di logica aziendale di produzione). Di solito trovo che ho dovuto usare l'approccio perché il design è un po 'sfortunato (cioè dover ripristinare le istanze singleton tra i test)
ChrisWue,

1
Sei tentato di sottovalutare questo: qual è la differenza tra questo hack e il fatto di rendere la classe interna anziché privata? Bene, almeno con i condizionali di compilazione. Quindi diventa davvero disordinato.
Bloke CAD

6
@CADbloke: Intendi rendere il metodo interno piuttosto che privato? La differenza è che è ovvio che realmente desidera che sia privato. Qualsiasi codice all'interno della tua base di codice di produzione che chiama un metodo con ForTestè ovviamente sbagliato, mentre se rendi il metodo interno sembra che vada bene usarlo.
Jon Skeet,

2
@CADbloke: è possibile escludere singoli metodi all'interno di una build di rilascio altrettanto facilmente nello stesso file dell'utilizzo di classi parziali, IMO. E se lo fai , suggerisce che non stai eseguendo i tuoi test contro la tua build di rilascio, che mi sembra una cattiva idea.
Jon Skeet,

11

Puoi usare anche private e puoi chiamare metodi privati ​​con reflection. Se stai usando Visual Studio Team Suite ha delle belle funzionalità che genereranno un proxy per chiamare i tuoi metodi privati ​​per te. Ecco un articolo di progetto in codice che dimostra come è possibile eseguire autonomamente il lavoro per testare i metodi privati ​​e protetti:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

In termini di quale modificatore di accesso dovresti usare, la mia regola generale è iniziare con privato ed intensificare secondo necessità. In questo modo esporrai tutti i dettagli interni della tua classe come sono realmente necessari e aiuta a mantenere nascosti i dettagli di implementazione, come dovrebbero essere.


3

Sto usando Dotnet 3.1.101e le .csprojaggiunte che hanno funzionato per me sono state:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

Spero che questo aiuti qualcuno là fuori!


1
L'aggiunta di informazioni esplicite sull'assemblaggio è stata ciò che alla fine ha funzionato anche per me. Grazie per aver postato questo!
thevioletsaber
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.