Come unit test dei metodi statici in C #

Quando si costruisce o si lavora in applicazioni .NET è spesso possibile utilizzare metodi statici. I metodi in C # possono essere statici o non statici. Un metodo non statico (noto anche come metodo di istanza) può essere invocato su un'istanza della classe a cui appartiene. I metodi statici non necessitano di un'istanza della classe per essere richiamati: possono essere chiamati sulla classe stessa.

Sebbene testare un metodo non statico (almeno uno che non chiama un metodo statico o interagisce con dipendenze esterne) sia semplice, testare un metodo statico non è affatto un compito facile. Questo articolo spiega come superare questa sfida e testare metodi statici in C #. 

[Anche su: Come eseguire il refactoring di oggetti Dio in C #]

Per lavorare con gli esempi di codice forniti in questo articolo, dovresti avere Visual Studio 2019 installato nel tuo sistema. Se non hai già una copia, puoi scaricare Visual Studio 2019 qui.  

Creare un progetto di applicazione console .NET Core in Visual Studio

Prima di tutto, creiamo un progetto di applicazione console .NET Core in Visual Studio. Supponendo che Visual Studio 2019 sia installato nel sistema, seguire i passaggi descritti di seguito per creare un nuovo progetto di applicazione console .NET Core in Visual Studio.

  1. Avvia l'IDE di Visual Studio.
  2. Fai clic su "Crea nuovo progetto".
  3. Nella finestra "Crea nuovo progetto", seleziona "App Console (.NET Core)" dall'elenco dei modelli visualizzati.
  4. Fare clic su Avanti.
  5. Nella finestra "Configura il tuo nuovo progetto" mostrata di seguito, specifica il nome e la posizione per il nuovo progetto.
  6. Fare clic su Crea. 

Ciò creerà un nuovo progetto di applicazione console .NET Core in Visual Studio 2019. In modo simile, creare altri due progetti: una libreria di classi e un progetto di unit test (xUnit test). Useremo questi tre progetti per illustrare i test unitari dei metodi statici nelle sezioni successive di questo articolo.

Quando un metodo statico può e non può essere testato in unità

Il test unitario di un metodo statico non è diverso dal test unitario di un metodo non statico. I metodi statici non sono verificabili di per sé. Un metodo statico che non contiene stato o non cambia stato può essere testato in unità. Finché il metodo e le sue dipendenze sono idempotenti, il metodo può essere testato in unità. I problemi sorgono quando il metodo statico chiama altri metodi o quando l'oggetto da testare chiama il metodo statico. D'altra parte, se l'oggetto da testare chiama un metodo di istanza, è possibile eseguire facilmente un test unitario.

Un metodo statico non può essere testato in unità se una delle seguenti condizioni è vera: 

  • Il metodo statico interagisce con dipendenze esterne come un database, un file system, una rete o un'API esterna.
  • Il metodo statico contiene le informazioni sullo stato, cioè se memorizza nella cache i dati in un oggetto statico della classe.

Considera il seguente frammento di codice che mostra due classi, ovvero ProductBL e Logger. Mentre ProductBL è una classe non statica, Logger è una classe statica. Notare che il metodo Write della classe Logger è stato chiamato dal metodo LogMessage della classe ProductBL.

classe pubblica ProductBL

    {

        public void LogMessage (stringa messaggio)

        {

            Logger.Write (messaggio);

        }

    }

    logger di classi pubbliche

    {

        public static void Write (string message)

        {

           // Scrivi qui il tuo codice per registrare i dati

        }

    }

Si supponga che il metodo Write della classe Logger si connetta a un database e quindi scriva i dati in una tabella di database. Il nome del database e la relativa tabella in cui devono essere scritti i dati potrebbero essere preconfigurati nel file appsettings.json. Come puoi ora scrivere unit test per il metodo ProductBL?

Nota che i metodi statici non possono essere derisi facilmente. Ad esempio, se hai due classi denominate A e B e la classe A utilizza un membro statico della classe B, non sarai in grado di testare l'unità di classe A in isolamento.

Tre modi per testare i metodi statici

È possibile utilizzare Moq per simulare metodi non statici ma non può essere utilizzato per simulare metodi statici. Sebbene i metodi statici non possano essere derisi facilmente, esistono alcuni modi per simulare i metodi statici.

Puoi sfruttare il framework Moles o Fakes di Microsoft per simulare le chiamate al metodo statico. (Il framework Fakes è stato incluso in Visual Studio 2012 come successore di Moles: è la generazione successiva di Moles e Stub.) Un altro modo per simulare le chiamate al metodo statico è usare i delegati. C'è ancora un altro modo per simulare le chiamate a metodi statici in un'applicazione: usando classi wrapper e inserimento di dipendenze.

IMHO quest'ultima opzione è la migliore soluzione al problema. Tutto quello che devi fare è racchiudere la chiamata al metodo statico all'interno di un metodo di istanza e quindi utilizzare l'inserimento delle dipendenze per iniettare un'istanza della classe wrapper nella classe sotto test.

Crea una classe wrapper in C #

Il frammento di codice seguente illustra la classe LogWrapper che implementa l'interfaccia IWrapper e racchiude una chiamata al metodo Logger.Write () all'interno di un metodo di istanza denominato LogData.

classe pubblica LogWrapper: IWrapper

    {

        string _message = null;

        public LogWrapper (messaggio di stringa)

        {

            _message = messaggio;

        }

        public void LogData (stringa messaggio)

        {

            _message = messaggio;

            Logger.Write (_message);

        }

    }

Il seguente frammento di codice mostra l'interfaccia di IWrapper. Contiene la dichiarazione del metodo LogData.

interfaccia pubblica IWrapper

    {

        void LogData (stringa messaggio);

    }

La classe ProductBL utilizza l'inserimento delle dipendenze (iniezione del costruttore) per iniettare un'istanza della classe LogWrapper come mostrato nell'elenco di codice riportato di seguito.

classe pubblica ProductBL

    {

        readonly IWrapper _wrapper;

        stringa statica _message = null;

        public ProductBL (IWrapper wrapper)

        {

            _wrapper = wrapper;

        }

        public void LogMessage (stringa messaggio)

        {

            _message = messaggio;

            _wrapper.LogData (_message);

        }

    }

Il metodo LogMessage della classe ProductBL chiama il metodo LogData sull'istanza della classe LogWrapper che è stata iniettata in precedenza.

Usare xUnit e Moq per creare un metodo di unit test in C #

Aprire il file UnitTest1.cs e rinominare la classe UnitTest1 in UnitTestForStaticMethodsDemo. I file UnitTest1.cs verrebbero automaticamente rinominati in UnitTestForStaticMethodsDemo.cs. Ora trarremo vantaggio dal framework Moq per impostare, testare e verificare i mock.

Il frammento di codice seguente illustra come usare il framework Moq per i metodi di unit test in C #.

var mock = new Mock ();

mock.Setup (x => x.LogData (It.IsAny ()));

new ProductBL (mock.Object) .LogMessage ("Hello World!");

mock.VerifyAll ();

Quando esegui il test, ecco come dovrebbe apparire l'output nella finestra Esplora test.

L'elenco completo del codice della classe di test è fornito di seguito come riferimento.

classe pubblica UnitTestForStaticMethodsDemo

    {

        [Fatto]

        public void StaticMethodTest ()

        {

            var mock = new Mock ();

            mock.Setup (x => x.LogData (It.IsAny ()));

            new ProductBL (mock.Object) .LogMessage ("Hello World!");

            mock.VerifyAll ();

        }

    }

Lo unit test è un processo che verifica le unità di codice in un'applicazione per verificare se i risultati effettivi dello unit test corrispondono ai risultati desiderati. Se usato con giudizio, i test unitari possono aiutare a prevenire bug nella fase di sviluppo di un progetto.

I metodi statici possono porre una serie di problemi quando si tenta di unit test utilizzando mock. Se la tua applicazione richiede di deridere un metodo statico, dovresti considerare che un odore di design - cioè, un indicatore di un cattivo design. Discuterò di mock, fake e stub in modo più dettagliato in un prossimo articolo qui.

Come fare di più in C #:

  • Come eseguire il refactoring di oggetti God in C #
  • Come usare ValueTask in C #
  • Come usare l'immutabilità in C
  • Come usare const, readonly e static in C #
  • Come usare le annotazioni dei dati in C #
  • Come lavorare con i GUID in C # 8
  • Quando usare una classe astratta e un'interfaccia in C #
  • Come lavorare con AutoMapper in C #
  • Come usare le espressioni lambda in C #
  • Come lavorare con i delegati Action, Func e Predicate in C #
  • Come lavorare con i delegati in C #
  • Come implementare un semplice logger in C #
  • Come lavorare con gli attributi in C #
  • Come lavorare con log4net in C #
  • Come implementare il modello di progettazione del repository in C #
  • Come lavorare con la riflessione in C #
  • Come lavorare con Filesystemwatcher in C #
  • Come eseguire l'inizializzazione pigra in C #
  • Come lavorare con MSMQ in C #
  • Come lavorare con i metodi di estensione in C #
  • Come utilizzare espressioni lambda in C #
  • Quando usare la parola chiave volatile in C #
  • Come utilizzare la parola chiave yield in C #
  • Come implementare il polimorfismo in C #
  • Come creare il proprio pianificatore di attività in C #
  • Come lavorare con RabbitMQ in C #
  • Come lavorare con una tupla in C #
  • Esplorazione di metodi virtuali e astratti in C #
  • Come utilizzare Dapper ORM in C #
  • Come utilizzare il modello di progettazione del peso mosca in C #