Come simulare i metodi statici in c # usando il framework MOQ?

Recentemente ho eseguito test di unità e ho simulato vari scenari con l’uso del framework MOQ e dei test MS creando test di unità. Come so, non possiamo testare metodi privati ​​ma usare il reflection ma voglio sapere come possiamo testare e simulare unit test usando il framework MOQ.

Moq (e altri framework di mocking basati su DynamicProxy ) non sono in grado di prendere in giro qualcosa che non sia un metodo virtuale o astratto.

Classi / metodi sigillati / statici possono essere simulati solo con strumenti basati su API di Profiler, come Typemock (commerciale) o Microsoft Moles (gratuito, noto come Fakes in Visual Studio 2012 Ultimate / 2013/2015 ).

In alternativa, puoi rifattorizzare il tuo design per astrarre le chiamate a metodi statici e fornire questa astrazione alla tua class tramite l’iniezione di dipendenza. Quindi non solo avresti un design migliore, sarà testabile con strumenti gratuiti, come Moq.

Un modello comune per consentire la testabilità può essere applicato senza utilizzare tutti gli strumenti del tutto. Considera il seguente metodo:

public class MyClass { public string[] GetMyData(string fileName) { string[] data = FileUtil.ReadDataFromFile(fileName); return data; } } 

Invece di provare a FileUtil.ReadDataFromFile , puoi racchiuderlo in un metodo protected virtual , come questo:

 public class MyClass { public string[] GetMyData(string fileName) { string[] data = GetDataFromFile(fileName); return data; } protected virtual string[] GetDataFromFile(string fileName) { return FileUtil.ReadDataFromFile(fileName); } } 

Quindi, nel test dell’unità, derivare da MyClass e chiamarlo TestableMyClass . Quindi è ansible sovrascrivere il metodo GetDataFromFile per restituire i propri dati di test.

Spero possa aiutare.

Un’altra opzione per trasformare il metodo statico in un Func o Action statico. Per esempio.

Codice originale:

  class Math { public static int Add(int x, int y) { return x + y; } 

Vuoi “deridere” il metodo Aggiungi, ma non puoi. Cambia il codice sopra a questo:

  public static Func Add = (x, y) => { return x + y; }; 

Il codice client esistente non deve essere modificato (magari ricompilato), ma l’origine rimane la stessa.

Ora, dal test unitario, per modificare il comportamento del metodo, basta riassegnare ad esso una funzione in linea:

  [TestMethod] public static void MyTest() { Math.Add = (x, y) => { return 11; }; 

Metti la logica che vuoi nel metodo o restituisci un valore codificato, a seconda di cosa stai cercando di fare.

Questo potrebbe non essere necessariamente qualcosa che puoi fare ogni volta, ma in pratica, ho trovato che questa tecnica funziona bene.

[modifica] Suggerisco di aggiungere il seguente codice di pulizia alla class di unit test:

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

Aggiungi una riga separata per ogni class statica. Ciò che fa è, dopo che il test dell’unità è stato eseguito, reimposta tutti i campi statici sul loro valore originale. In questo modo, gli altri test di unità nello stesso progetto inizieranno con i valori predefiniti corretti rispetto alla versione derisa.

Moq non può prendere in giro un membro statico di una class.

Quando si progetta il codice per la testabilità è importante evitare membri statici (e singleton). Un modello di progettazione che può aiutarti a rielaborare il tuo codice per la testabilità è Iniezione delle dipendenze.

Questo significa cambiare questo:

 public class Foo { public Foo() { Bar = new Bar(); } } 

a

 public Foo(IBar bar) { Bar = bar; } 

Questo ti permette di usare una simulazione dai tuoi test unitari. Nella produzione si utilizza uno strumento di iniezione delle dipendenze come Ninject o Unity che può colbind tutto insieme.

Ho scritto un blog su questo qualche tempo fa. Spiega quali pattern devono essere utilizzati per un codice testabile migliore. Forse può aiutarti: unit test, inferno o paradiso?

Un’altra soluzione potrebbe essere l’utilizzo di Microsoft Fakes Framework . Questo non è un sostituto per la scrittura di un codice testabile ben progettato, ma può aiutarti. Il framework Fakes ti permette di deridere i membri statici e sostituirli in runtime con il tuo comportamento personalizzato.

Come menzionato nelle altre risposte, MOQ non può prendere in giro metodi statici e, come regola generale, si dovrebbe evitare la statica laddove ansible.

A volte non è ansible. Uno sta lavorando con legacy o codice di terze parti o addirittura con i metodi BCL statici.

Una ansible soluzione è avvolgere la statica in un proxy con un’interfaccia che può essere derisa

  public interface IFileProxy { void Delete(string path); } public class FileProxy : IFileProxy { public void Delete(string path) { System.IO.File.Delete(path); } } public class MyClass { private IFileProxy _fileProxy; public MyClass(IFileProxy fileProxy) { _fileProxy = fileProxy; } public void DoSomethingAndDeleteFile(string path) { // Do Something with file // ... // Delete System.IO.File.Delete(path); } public void DoSomethingAndDeleteFileUsingProxy(string path) { // Do Something with file // ... // Delete _fileProxy.Delete(path); } } 

Il rovescio della medaglia è che il ctor può diventare molto disordinato se ci sono molti proxy (anche se si potrebbe sostenere che se ci sono molti proxy, la class potrebbe provare a fare troppo e potrebbe essere refactored)

Un’altra possibilità è di avere un “proxy statico” con diverse implementazioni dell’interfaccia dietro di esso

  public static class FileServices { static FileServices() { Reset(); } internal static IFileProxy FileProxy { private get; set; } public static void Reset(){ FileProxy = new FileProxy(); } public static void Delete(string path) { FileProxy.Delete(path); } } 

Il nostro metodo ora diventa

  public void DoSomethingAndDeleteFileUsingStaticProxy(string path) { // Do Something with file // ... // Delete FileServices.Delete(path); } 

Per il test, possiamo impostare la proprietà FileProxy sulla nostra simulazione. L’uso di questo stile riduce il numero di interfacce da iniettare, ma rende le dipendenze un po ‘meno ovvie (anche se non più delle chiamate statiche originali suppongo).