È statico universalmente "malvagio" per il test delle unità e, in tal caso, perché lo consiglia? [chiuso]

82

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

Dato che due di questi non sono gratuiti e uno non ha colpito la versione 1.0, la simulazione delle cose statiche non è troppo facile.

Questo rende i metodi statici e tali "cattivi" (nel senso del test unitario)? E se è così, perché il resharper vuole che io faccia qualcosa che può essere statico, statico? (Supponendo che il resharper non sia anche "malvagio".)

Chiarimento: Sto parlando dello scenario quando vuoi testare un metodo unitario e quel metodo chiama un metodo statico in un'unità / classe diversa . Con la maggior parte delle definizioni dei test unitari, se lasci che il metodo sotto test chiami il metodo statico nell'altra unità / classe, allora sei non test delle unità, sei integration testing . (Utile, ma non un test unitario.)

    
posta Vaccano 21.09.2010 - 03:18
fonte

11 risposte

102

Guardando le altre risposte qui, penso che ci possa essere una certa confusione tra i metodi statici che mantengono lo stato statico o causano effetti collaterali (che a me sembrano una pessima idea), e metodi statici che si limitano a restituire un valore.

I metodi statici che non contengono nessuno stato e non causano effetti collaterali dovrebbero essere facilmente testabili. In effetti, considero tali metodi una forma di programmazione funzionale da "uomo povero"; passi il metodo a un oggetto o valore e restituisce un oggetto o un valore. Niente di più. Non vedo come tali metodi influenzino negativamente il test delle unità.

    
risposta data 21.09.2010 - 17:00
fonte
24

Sembra che tu stia confondendo i dati statici e i metodi statici. Il richiama, se non ricordo male, raccomanda di fare i metodi di private all'interno di una classe statica se possono essere fatti così - credo che questo produca un piccolo vantaggio in termini di prestazioni. non consiglia di creare "tutto ciò che può essere" statico!

Non c'è niente di sbagliato nei metodi statici e sono facili da testare (a patto che non cambino i dati statici). Ad esempio, pensate a una libreria di matematica, che è un buon candidato per una classe statica con metodi statici. Se hai un metodo (forzato) come questo:

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

allora questo è eminentemente testabile e non ha effetti collaterali. Devi solo controllare che quando passi, diciamo, 20 torni a 400. Nessun problema.

    
risposta data 21.09.2010 - 17:29
fonte
17

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

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

Quindi, basta rifattorizzare il codice e iniettare come al solito la chiamata alla classe statica come questa:

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();
    }
}
    
risposta data 15.08.2011 - 15:26
fonte
14

Controlla questo: "I metodi statici sono mortali per la testabilità" . Breve riassunto dell'argomento:

Al test unitario è necessario prendere una piccola parte del codice, ricollegare le sue dipendenze e verificarlo da solo. Questo è difficile con i metodi statici, non solo nel caso in cui accedono allo stato globale ma anche se chiamano solo altri metodi statici.

    
risposta data 21.09.2010 - 09:37
fonte
13

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

Ci sono due approcci generali per il mocking.

Il primo (tradizionale - implementato da RhinoMocks, Moq, NMock2; anche i mock e gli stub manuali sono in questo campo) fa affidamento sulle cuciture dei test e sull'iniezione delle dipendenze. Supponiamo che tu stia testando unitamente un codice statico e che abbia delle dipendenze. Quello che succede spesso nel codice progettato in questo modo è che le statiche creano le loro dipendenze, invertendo l'inversione di dipendenza . Scoprirai presto che non puoi iniettare interfacce derise nel codice sotto test progettato in questo modo.

Il secondo (mocking qualsiasi cosa - implementato da TypeMock, JustMock e Moles) si affida a .NET API di profiling . Può intercettare 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 prendere in giro 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 SOLID e progetta per la testabilità (che spesso include la facilità con la statica). L'altro dice, acquista TypeMock e non preoccuparti.

    
risposta data 21.09.2010 - 19:23
fonte
5

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

Ma non avrà la chiave che definisce le proprietà di un test; fallire quando le cose vanno male, passare quando hanno ragione.

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

Dato che ci sono tre opzioni:

  1. definisce il test delle unità per testare l'unità composta da una classe e le sue dipendenze hard-coded. Funziona, assicurandoti di evitare le dipendenze circolari.

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

  3. non eseguire test unitario, test di integrazione. Che funziona, a condizione che non sia in conflitto con qualcos'altro che è necessario utilizzare il termine test di integrazione per.

risposta data 01.03.2015 - 21:07
fonte
4

Non ci sono due modi per farlo. I suggerimenti di ReSharper e alcune utili funzionalità di C # non sarebbero state usate così spesso se stavi scrivendo test di unità atomiche isolati per tutto il tuo codice.

Ad esempio, se si dispone di un metodo statico e si deve eseguire lo stub, non è possibile a meno che non si utilizzi un framework di isolamento basato su profilo. Una soluzione alternativa compatibile con la chiamata consiste nel modificare la parte superiore del metodo per utilizzare la notazione lambda. Ad 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 di Unit-Test, puoi stub questa chiamata in questo modo (supponendo che si trovi 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-up del tuo unit test, quello che viene chiamato dopo ogni test, scrivi un codice come questo:

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

che invocherà nuovamente l'inizializzatore statico della tua classe.

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

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

Ma diciamo che si evita del tutto la statistica e si converte questo in un metodo di istanza. Non è ancora discutibile a meno che il metodo non sia virtuale o implementato come parte di un'interfaccia.

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

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

Se utilizzi una di queste "Funzioni", non puoi semplicemente creare metodi isolati.

Quindi quando li usi?

Usali per qualsiasi codice che non ti aspetti che qualcuno voglia mai spegnere. Qualche esempio:     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 si può parlare delle prestazioni di una chiamata indiretta, questa è un'altra ragione per evitare i metodi di istanza. Ci sono casi in cui è un successo in termini di prestazioni. Ecco perché i metodi non virtuali esistono in primo luogo.

    
risposta data 01.03.2015 - 15:42
fonte
3
  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é questo odora di micro ottimizzazione) vedi link
  2. Ti sta dicendo che non ha bisogno di stato, quindi potrebbe essere chiamato da qualsiasi luogo, rimuovendo l'overhead di instatiation se è l'unica cosa di cui qualcuno ha bisogno.
  3. Se voglio prenderlo in giro, penso che in genere è la prassi 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à di renderlo statico.
  6. Lo stato di mantenimento (campi) staticamente è qualcosa che dovrebbe essere sempre considerato attentamente . Stato statico e fili si mescolano come il litio e l'acqua.

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

In generale, direi che se il metodo è statico, in genere dovrebbe essere testabile. Questo porta qualche considerazione sul design e probabilmente più discussioni di quante ne abbia tra le mie dita in questo momento, così pazientemente in attesa dei voti e dei commenti negativi ...;)

    
risposta data 21.09.2010 - 05:38
fonte
3

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 che mi dice: "hey, tu, questi pezzi di logica non sono la RESPONSABILITÀ della classe corrente da gestire, quindi dovrebbe stare fuori in qualche classe di supporto o qualcosa di simile ".

    
risposta data 29.11.2012 - 18:08
fonte
2

Se il metodo statico viene chiamato da all'interno di un altro metodo , non è possibile impedire o sostituire tale chiamata. Questo significa che questi due metodi formano una singola unità; Il test unitario di qualsiasi tipo li testa entrambi.

E se questo metodo statico parla con Internet, collega i database, mostra i popup della GUI o converte in altro modo il test delle unità in un disastro completo, semplicemente senza alcun lavoro facile. Un metodo che chiama tale metodo statico non è testabile senza refactoring, anche se ha un sacco di codice puramente computazionale che trarrebbe grande beneficio dall'essere testato unitamente.

    
risposta data 18.10.2015 - 09:20
fonte
0

Credo che Resharper ti dia la sicurezza e applichi le linee guida di codifica con cui è stato configurato. 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 alcuna variabile di istanza.

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

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 manterrò tutto questo al minimo e userei i metodi statici come pure le funzioni il più possibile dove tutte le dipendenze sono passate nel metodo che può essere controllato tramite un dispositivo di prova. Tuttavia questa è una decisione di progettazione.

    
risposta data 21.09.2010 - 05:38
fonte

Leggi altre domande sui tag