Domanda

Voglio gestire un'eccezione ManagementException solo per un ErrorCode specifico e ho problemi a scrivere il test unitario per esso. Di solito, scriverei il test in modo che sia simile al seguente:

Searcher search = MockRepository.GenerateMock<Searcher>(); 
// wrapper for ManagementObjectSearcher

...

search.Expect(s => s.Get()).Throw(new ManagementException());

...

Tuttavia, questo non imposta ErrorCode su quello che voglio in particolare, anzi ManagementException non ha un costruttore che imposta questo valore.

Come si può fare?

(Nota che sto usando RhinoMocks come il mio framework beffardo ma sto assumendo che questo sia indipendente dal framework; tutto quello che devo sapere qui è come creare un ManagementException che abbia un valore ErrorCode specifico. Inoltre ho trovato alcuni riferimenti a un metodo System.Management.ManagementException.ThrowWithExtendedInfo (ManagementStatus errorCode) online ma questo non sembra essere accessibile pubblicamente).

È stato utile?

Soluzione

Il minimo sforzo per superare questo ostacolo sarebbe un metodo di supporto / utilità statico che utilizza la riflessione per hackerare lo slot nel codice di errore richiesto. Utilizzando il riflettore più eccellente, vedo che esiste un "errorCode" privato campo, che viene impostato solo tramite i registri interni definiti in ManagementException. Quindi :)

public static class EncapsulationBreaker
   {
      public static ManagementException GetManagementExceptionWithSpecificErrorCode(ManagementStatus statusToBeStuffed)
      {
         var exception = new ManagementException();
         var fieldInfo = exception.GetType().GetField("errorCode", 
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.DeclaredOnly);
         fieldInfo.SetValue(exception, statusToBeStuffed);
         return exception;
      }
   }

Verificato che funziona

[Test]
      public void TestGetExceptionWithSpecifiedErrorCode()
      {
         var e = EncapsulationBreaker.GetManagementExceptionWithSpecificErrorCode(ManagementStatus.BufferTooSmall);
         Assert.AreEqual(ManagementStatus.BufferTooSmall, e.ErrorCode);
      }

Anche se generalmente disapprovo la riflessione nei test , questo è uno dei rari casi in cui è necessario / utile.
HTH

Altri suggerimenti

Deriva una classe da ManagementException e nascondi l'implementazione del codice di errore con la tua. Fai in modo che il tuo mock restituisca questa classe.

Avere un metodo o una classe molto semplice e di piccole dimensioni che rileva tale eccezione e ne ricava il codice di errore, quindi lo passa alla classe reale che fa il lavoro. Sotto test, sostituisci quel codice con passare direttamente alla classe reale ciò che passeresti quando otterrai quel codice di errore.

Il modo più ovvio è sottoclassare l'eccezione, ma se non funziona, allora il codice che la cattura e genera immediatamente la tua eccezione che ti consente di esporre quel codice sarebbe un'altra opzione.

Vorrei sottoclassare ManagementException e nella sottoclasse ignorare il getter ErrorCode (se i normali livelli di protezione ti impediscono di farlo, forse l'introspezione può avvicinarti). Qualsiasi codice che gestisca ManagementException ma non abbia mai sentito parlare della tua sottoclasse specifica dovrebbe gestire la sottoclasse "come se" è stato il ManagementException che stai cercando di simulare a scopo di test, dopo tutto.

Modifica : è ipotizzabile che ErrorCode non possa essere sovrascritto (odio le lingue così rigide che possono smettere di testare in questo modo, ma non posso negare che esistano; -). In questo caso, Iniezione di dipendenza può ancora salvarti: DI è uno dei miei modelli preferiti per i test.

Lo scopo di DI nei test è quello di disaccoppiare il codice sotto test da assunzioni rigide che inibirebbero la testabilità - ed è proprio quello che abbiamo qui, sebbene in una forma insolita. Il tuo codice in prova attualmente, ad esempio, x.ErrorCode per ottenere il codice di errore dell'eccezione x. Molto bene, deve invece fare getErrorCode (x) dove getErrorCode è un delegato che normalmente restituisce semplicemente restituendo x.ErrorCode ; e deve avere un setter per il delegato getErrorCode , in modo che a scopo di test, puoi cambiarlo in un delegato che restituisce 23 (o qualunque valore del codice di errore tu abbia vuoi simulare per i test).

I dettagli possono variare, ma l'iniezione di dipendenza (tra le altre cose) può aiutare a compensare alcuni tipi di rigidità eccessiva negli oggetti che inevitabilmente si ottengono dal sistema (o da altre librerie e c) che non è possibile modificare direttamente), come in questo esempio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top