it-swarm.it

L'elettricità statica è universalmente "malvagia" per i test unitari e, in caso affermativo, perché Resharper lo consiglia?

Ho scoperto che ci sono solo 3 modi per dipendenze unit test (mock/stub) statici in C # .NET:

Dato che due di questi non sono gratuiti e uno non ha raggiunto la versione 1.0, prendere in giro cose statiche non è troppo facile.

Questo ha metodi statici e tale "male" (nel senso del test unitario)? E se è così, perché il resharper vuole che io faccia qualcosa che possa essere statico, statico? (Supponendo che il resharper non sia anche "malvagio".)

Chiarimento: Sto parlando dello scenario in cui vuoi testare un metodo su un metodo e quel metodo chiama un metodo statico in un diverso unità/classe. In base alla maggior parte delle definizioni di unit test, se si lascia semplicemente che il metodo in esame chiami il metodo statico nell'altra unità/classe, allora si sta non unit test, sei integrazione test. (Utile, ma non un test unitario.)

87
Vaccano

Guardando le altre risposte qui, penso che potrebbe esserci un po 'di confusione tra metodi statici che mantengono lo stato statico o causano effetti collaterali (che mi sembra una cattiva idea) e metodi statici che semplicemente restituiscono un valore.

I metodi statici che non mantengono alcuno stato e non causano effetti collaterali dovrebbero essere facilmente testabili dall'unità. In effetti, considero tali metodi una forma di programmazione funzionale "da uomo povero"; si passa al metodo un oggetto o un valore e restituisce un oggetto o un valore. Niente di più. Non vedo come tali metodi influirebbero negativamente sui test unitari.

108
Robert Harvey

Sembra che tu stia confondendo static data e static metodi. Resharper, se ricordo bene, raccomanda di rendere statici i metodi private se possono essere fatti in questo modo - credo che ciò produca un piccolo vantaggio in termini di prestazioni. non consiglia di rendere "tutto ciò che può essere" statico!

Non c'è nulla di sbagliato nei metodi statici e sono facili da testare (purché non modifichino dati statici). Ad esempio, pensa a una libreria matematica, che è un buon candidato per una classe statica con metodi statici. Se hai un metodo (inventivo) come questo:

public static long Square(int x)
{
    return x * x;
}

allora questo è eminentemente testabile e non ha effetti collaterali. Basta controllare che quando si passa, diciamo, 20 si torna indietro 400. Nessun problema.

27
Dan Diplo

Se la vera domanda qui è "Come testare questo codice?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Quindi, basta riformattare il codice e iniettare come al solito la chiamata alla classe statica in questo modo:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
18
Sunny

Le statistiche non sono necessariamente malvagie, ma possono limitare le opzioni quando si tratta di test unitari con falsi/mock/stub.

Esistono due approcci generali alla derisione.

Il primo (tradizionale - implementato da RhinoMocks, Moq, NMock2; in questo campo si trovano anche mock e tronconi manuali) si basa su cuciture di prova e iniezione di dipendenza. Supponiamo che tu stia testando un po 'di codice statico e abbia dipendenze. Ciò che accade spesso nel codice progettato in questo modo è che la statica crea le proprie dipendenze, invertendo inversione di dipendenza . Scoprirai presto che non puoi iniettare interfacce derise nel codice sotto test progettato in questo modo.

Il secondo (finto tutto - implementato da TypeMock, JustMock e Moles) si basa su .NET Profiling API . Può intercettare una qualsiasi delle tue istruzioni CIL e sostituire un pezzo del tuo codice con un falso. Ciò consente a TypeMock e ad altri prodotti in questo campo di deridere qualsiasi cosa: statica, classi sigillate, metodi privati ​​- cose non progettate per essere testabili.

C'è un dibattito in corso tra due scuole di pensiero. Uno dice, segui i principi SOLIDI e progetta per la testabilità (che spesso include andare piano con la statica). L'altro dice, compra TypeMock e non preoccuparti.

15
azheglov

Dai un'occhiata a questo: "I metodi statici sono dalla morte alla testabilità" . Breve sommario dell'argomento:

Per unit test è necessario prendere una piccola parte del codice, ricollegare le sue dipendenze e testarlo in modo isolato. Questo è difficile con i metodi statici, non solo nel caso in cui accedano allo stato globale ma anche se chiamano semplicemente altri metodi statici.

14
Rafał Dowgird

La semplice verità raramente riconosciuta è che se una classe contiene una dipendenza visibile dal compilatore su un'altra classe, non può può essere testata separatamente da quella classe. Puoi falsificare qualcosa che assomiglia a un test e verrà visualizzato in un rapporto come se fosse un test.

Ma non avrà la chiave che definisce le proprietà di un test; fallendo quando le cose sono sbagliate, passando quando sono giuste.

Questo vale per qualsiasi chiamata statica, chiamata del costruttore e qualsiasi riferimento a metodi o campi non ereditati da una classe o interfaccia base . Se il nome della classe appare nel codice, è una dipendenza visibile dal compilatore e non puoi validamente testare senza di essa. Qualsiasi pezzo più piccolo è semplicemente non un'unità testabile valida. Qualsiasi tentativo di trattarlo come se avesse risultati non più significativi della scrittura di una piccola utility per emettere l'XML utilizzato dal framework di test per dire "test superato".

Detto questo, ci sono tre opzioni:

  1. definire test unitari per testare l'unità composta da una classe e dalle sue dipendenze codificate. Funziona, a condizione di evitare dipendenze circolari.

  2. non creare mai dipendenze in fase di compilazione tra le classi di cui sei responsabile per il test. Funziona, a condizione che non ti dispiaccia lo stile di codice risultante.

  3. non test unitario, test di integrazione invece. Che funziona, a condizione che non sia in conflitto con qualcos'altro per cui devi usare il termine test di integrazione.

5
soru

Non ci sono due modi per farlo. I suggerimenti di ReSharper e diverse utili funzioni di C # non verrebbero usati così spesso se scrivessi test di unità atomiche isolati per tutto il tuo codice.

Ad esempio, se si dispone di un metodo statico e è necessario eliminarlo, non è possibile a meno che non si utilizzi un framework di isolamento basato sul profilo. Una soluzione alternativa compatibile con le chiamate consiste nel modificare la parte superiore del metodo per utilizzare la notazione lambda. Per esempio:

PRIMA:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

DOPO:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

I due sono compatibili con le chiamate. I chiamanti non devono cambiare. Il corpo della funzione rimane lo stesso.

Quindi nel tuo codice Unit-Test, puoi stub questa chiamata in questo modo (supponendo che sia in una classe chiamata Database):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Fai attenzione a sostituirlo con il valore originale dopo aver finito. Puoi farlo tramite un try/finally o, nel clean-test dell'unità, quello che viene chiamato dopo ogni test, scrivere un codice come questo:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

che richiamerà l'inizializzatore statico della tua classe.

I Lambda Func non sono così ricchi di supporto come i normali metodi statici, quindi questo approccio ha i seguenti effetti collaterali indesiderati:

  1. Se il metodo statico era un metodo di estensione, è necessario prima modificarlo in un metodo non di estensione. Resharper può farlo automaticamente per te.
  2. Se uno dei tipi di dati dei metodi statici è un assembly con interoperabilità incorporato, ad esempio per Office, è necessario racchiudere il metodo, avvolgere il tipo o modificarlo per digitare 'oggetto'.
  3. Non è più possibile utilizzare lo strumento di refactoring per la firma delle modifiche di Resharper.

Ma supponiamo che tu eviti del tutto la statica e la converti in un metodo di istanza. Non è ancora derisorio a meno che il metodo non sia virtuale o implementato come parte di un'interfaccia.

Quindi, in realtà, chiunque suggerisca il rimedio per eliminare i metodi statici è di renderli metodi di istanza, sarebbero anche contro metodi di istanza che non sono virtuali o che fanno parte di un'interfaccia.

Quindi perché C # ha metodi statici? Perché consente metodi di istanza non virtuali?

Se si utilizza una di queste "Funzionalità", semplicemente non è possibile creare metodi isolati.

Quindi quando li usi?

Usali per qualsiasi codice che non ti aspetti che qualcuno voglia mai stub. Alcuni esempi: il metodo Format () della classe String il metodo WriteLine () della classe Console il metodo Cosh () della classe Math

E un'altra cosa ... La maggior parte delle persone non si preoccuperà di questo, ma se è possibile eseguire una chiamata indiretta, questo è un altro motivo per evitare i metodi di istanza. Ci sono casi in cui si tratta di un successo prestazionale. Ecco perché esistono in primo luogo metodi non virtuali.

4
zumalifeguard

Vedo che dopo molto tempo nessuno ha ancora dichiarato un fatto davvero semplice. Se il resharper mi dice che posso rendere statico un metodo, significa una cosa enorme per me, posso sentire la sua voce dirmi: "hey, tu, questi pezzi di logica non sono RESPONSABILITÀ della classe corrente da gestire, quindi dovrebbe rimanere fuori in una classe di aiuto o qualcosa del genere ".

3
g1ga
  1. Credo che sia in parte dovuto al fatto che i metodi statici sono "più veloci" da chiamare rispetto ai metodi di istanza. (Tra virgolette perché puzza di micro ottimizzazione) vedi http://dotnetperls.com/static-method
  2. Ti sta dicendo che non ha bisogno di stato, quindi potrebbe essere chiamato da qualsiasi luogo, rimuovendo l'instatazione ambientale se questa è l'unica cosa di cui qualcuno ha bisogno.
  3. Se voglio deriderlo, allora penso che sia generalmente la pratica che viene dichiarata su un'interfaccia.
  4. Se è dichiarato su un'interfaccia, R # non ti suggerirà di renderlo statico.
  5. Se è dichiarato virtuale, R # non ti suggerirà nemmeno di renderlo statico.
  6. Mantenere staticamente (campi) staticamente è qualcosa che dovrebbe sempre essere considerato con attenzione . Stato statico e fili si mescolano come litio e acqua.

R # non è l'unico strumento che fornirà questo suggerimento. Anche l'analisi del codice FxCop/MS farà lo stesso.

Direi generalmente che se il metodo è statico, in genere dovrebbe essere testabile. Ciò porta un po 'di considerazione sul design e probabilmente più discussioni di quelle che ho tra le dita in questo momento, quindi aspetto pazientemente i voti negativi e i commenti ...;)

3
MIA

Se il metodo statico viene chiamato da all'interno di un altro metodo, non è possibile impedire o sostituire tale chiamata. Ciò significa che questi due metodi formano un'unica unità; Test unitari di qualsiasi tipo li testano entrambi.

E se questo metodo statico parla a Internet, collega i database, mostra i popup della GUI o converte in altro modo il test dell'unità in un casino completo, non fa altro che aggirare il problema. Un metodo che chiama tale metodo statico non è testabile senza refactoring, anche se ha un sacco di codice puramente computazionale che trarrebbe grandi benefici dal test unitario.

2
h22

Credo che Resharper ti dia prova e applichi le linee guida di codifica con cui è stato impostato. Quando ho usato Resharper e mi ha detto che un metodo dovrebbe essere statico, è destinato ad essere su un metodo privato che non agisce su nessuna variabile di istanza.

Ora per quanto riguarda la testabilità questo scenario non dovrebbe essere un problema in quanto non dovresti testare metodi privati ​​comunque.

Per quanto riguarda la testabilità dei metodi statici che sono pubblici, i test unitari diventano difficili quando i metodi statici toccano lo stato statico. Personalmente lo terrei al minimo e userò i metodi statici il più possibile come pure funzioni in cui tutte le dipendenze vengono passate al metodo che può essere controllato tramite un dispositivo di prova. Tuttavia questa è una decisione di progettazione.

0
aqwert