Come non testare l'implementazione quando il metodo restituisce void?

1

Molti luoghi nello stato di stackexchange non devono essere implementati con test unitari, ma solo l'interfaccia pubblica di una classe. Ma cosa succede se l'interfaccia pubblica è un metodo che non restituisce alcun valore, ad esempio un metodo pubblico SendMailNotifications() con una certa logica che ha una logica interna (metodi privati) e invia email di destinatari diversi - Vorrei aggiungere test quell'email viene inviata ai destinatari corretti in base alla mia logica aziendale.

L'unico modo in cui potrei pensare è convertire i metodi privati in protetti e poi Verify () che vengono chiamati. Ma questo sembra contraddire lo 'spirito' dei test unitari in cui non si dovrebbero testare metodi di implementazione \ private (e per convertire i metodi privati in protetti solo per motivi di testing).

C'è un modo migliore (un modo che è più nello "spirito" dei test unitari) per realizzare questo? Forse il codice stesso dovrebbe essere rafactored per questo scopo? Se sì, come?

    
posta BornToCode 25.10.2016 - 19:47
fonte

1 risposta

3

Dipendenza dalla dipendenza e derisione è tuo amico. Ogni volta che trovi qualcosa di difficile da testare, di solito significa che la tua classe ha troppe responsabilità. Suppongo che tu stia inviando le tue email usando una libreria. Probabilmente questa libreria prende le informazioni del tuo messaggio (indirizzo di destinazione, oggetto, corpo). Quello che vuoi fare è disgiungere la tua logica di business che è incaricata di raccogliere tutti gli indirizzi email, l'oggetto e il corpo dell'e-mail, da quella libreria di terze parti. Come? Sottragliando quella libreria usando un'interfaccia, e poi inserendola nella tua classe Email.

IEmailLibrary
{
   void SendEmail(EmailInfo emailInfo);
}

class Email
{
   private IEmailLibrary _emailLibrary;

   public Email(IEmailLibrary emailLibrary)
   {
      _emailLibrary = emailLibrary;
   }

   public void SendEmailNotifications()
   {
      EmailInfo emailInfo = ComposeEmail(); 
      _emailLibrary.SendEmail(emailInfo);
   }

   private EmailInfo ComposeEmail()
   {
      //your business logic
   }
}

Ora nel tuo codice di produzione, dovresti creare una vera e-mailLibrary come questa:

public RealEmailLibrary : IEmailLibrary
{
   ...

   public SendEmail(EmailInfo emailInfo)
   {
      _3rdPartyEmailComponent.SendEmail(emailInfo....);
   }
}

e quindi inseriscilo nella tua classe di email, in questo modo:

RealEmailLibrary realEmailLibrary = new RealEmailLibrary();
Email email = new email(realEmailLibrary);

E se volevi testare la tua logica di business, tutto ciò che devi fare nel tuo codice di test, è inserire un finto, o il tuo stub che implementa un'interfaccia IEmailLibrary. Ad esempio:

public FakeEmailLibrary : IEmailLibrary
{
   public EmailInfo _iWasCalledWithThisEmailInfo;

   public void SendEmail(EmailInfo emailInfo)
   {
      _iWasCalledWithThisEmailInfo = emailInfo;
   }
}

L'hai iniettato nel test in questo modo:

FakeEmailLibrary fakeEmailLibrary = new FakeEmailLibrary();
Email email = new email(fakeEmailLibrary);
email.SendEmailNotifications();
AssertStuff(fakeEmailLibrary.iWasCalledWithThisEmailInfo, expectedEmailInfo);

Raccomando l'uso di framework di simulazione, al contrario del mio esempio di stub (come Moq for C #), che rende molto semplice verificare che le dipendenze iniettate siano state chiamate con gli argomenti giusti.

    
risposta data 26.10.2016 - 05:48
fonte

Leggi altre domande sui tag