2012-05-08 15 views
8

ho 2 classi:Come stub scrittura con NUnit in C#

  • FirstDeep.cs
  • SecondDeep.cs

    ho fatto semplice codice per esempio:

 

class FirstDeep 
    { 
     public FirstDeep() { } 

     public string AddA(string str) 
     { 
      SecondDeep sd = new SecondDeep(); 
      bool flag = sd.SomethingToDo(str); 

      if (flag == true) 
       str = string.Concat(str, "AAA"); 
      else 
       str = string.Concat(str, "BBB"); 

      return str; 
     } 
    } 
 

e

class SecondDeep 
    { 
     public bool SomethingToDo(string str) 
     { 
      bool flag = false; 
      if (str.Length < 10) 
      { 
       //todo something in DB, and after that flag should be TRUE 
      } 
      return flag; 
     } 
    } 

Poi voglio scrivere unit test per il metodo "Adda":

class Tests 
    { 
     [Test] 
     public void AddATest() 
     { 
      string expected = "ABCAAA"; 

      FirstDeep fd = new FirstDeep(); 
      string res = fd.AddA("ABC"); 

      Assert.AreEqual(expected, res); 
     } 
    } 

E dopo che ho problemi, io non so come corretta stub scrittura per il metodo SomethingToDo nella mia classe di test. Ho sempre false. Dovrei solo tornare VERO. Ma come?

+0

si può ancora lavorare con il vostro modello: introdurre 'bool previsto = false; SecondDeep sd = new SecondDeep(); bool actualResult = sd.SomethingToDo ("ABC"); Assert.AreEqual (excpected, actualResult); '...!? se questo non soddisfa le tue esigenze, probabilmente dovresti considerare di elaborare e migliorare la tua domanda! –

+0

Hai eseguito il debug del tuo codice? Se sta accadendo nel database, non possiamo aiutarti perché non abbiamo dettagli su ciò che sta accadendo lì. – Jon

+0

Sì, ho eseguito il debug del mio codice e dove ho scritto: "// todo qualcosa in DB, e dopo questo flag dovrebbe essere TRUE" Io uso class MembershipUser da .NET e questo metodo non può connettersi a DB, ecco perché dovrei ritorna vero su questo caso. – Smit

risposta

9

Un buon modo per consentire di scrivere stub è utilizzare l'iniezione di dipendenza . FirstDeep dipende da SecondDeep e nel test si desidera sostituire SecondDeep con uno stub.

prima cambiare il codice esistente con l'estrazione di un'interfaccia per SecondDeep e poi iniettare che in FirstDeep nel costruttore:

interface ISecondDeep { 

    Boolean SomethingToDo(String str); 

} 

class SecondDeep : ISecondDeep { ... } 

class FirstDeep { 

    readonly ISecondDeep secondDeep; 

    public FirstDeep(ISecondDeep secondDeep) { 
    this.secondDeep = secondDeep; 
    } 

    public String AddA(String str) { 
    var flag = this.secondDeep.SomethingToDo(str); 
    ... 
    } 

} 

noti che FirstDeep non crea un'istanza SecondDeep. Invece un'istanza è iniettata nel costruttore.

Nella tua prova è possibile creare uno stub per ISecondDeep dove SomethingToDo restituisce sempre vero:

class SecondDeepStub : ISecondDeep { 

    public Boolean SomethingToDo(String str) { 
    return true; 
    } 

} 

Nel test si utilizza lo stub:

var firstDeep = new FirstDeep(new SecondDeepStub()); 

Nel codice di produzione si utilizza il "vero "SecondDeep:

L'uso di un contenitore per le dipendenze e di un framework di stub può rendere molto più facile farlo.

Se non si desidera riscrivere il codice, è possibile utilizzare un framework per intercettare chiamate come Microsoft Moles. Nella prossima versione di Visual Studio sarà disponibile una tecnologia simile nel Fakes Framework.

4

Per rendere testabile il codice, non istanziare dipendenze all'interno della classe. Utilizzare dependency injection (tramite costruttore, proprietà o parametro). Anche utilizzare classi astratte o interfacce per consentire beffardo delle dipendenze:

class FirstDeep 
{ 
    private ISecondDeep oa; 

    public FirstDeep(ISecondDeep oa) 
    { 
     this.oa = oa; 
    } 

    public string AddA(string str) 
    { 
     return String.Concat(str, oa.SomethingToDo(str) ? "AAA" : "BBB"); 
    } 
} 

A seconda delle astrazioni permette di testare la vostra classe in isolamento.

interface ISecondDeep 
{ 
    bool SomethingToDo(string str); 
} 

class SecondDeep : ISecondDeep 
{ 
    public bool SomethingToDo(string str) 
    { 
     bool flag = false; 
     if (str.Length < 10) 
     { 
      // without abstraction your test will require database 
     } 
     return flag; 
    } 
} 

Ecco campione (utilizzando Moq).Essa mostra come è possibile tornare true dalla chiamata alla dipendenza deriso:

[TestFixture] 
class Tests 
{ 
    [Test] 
    public void AddAAATest() 
    { 
     // Arrange 
     Mock<ISecondDeep> secondDeep = new Mock<ISecondDeep>(); 
     secondDeep.Setup(x => x.SomethingToDo(It.IsAny<string>())).Returns(true); 
     // Act 
     FirstDeep fd = new FirstDeep(secondDeep.Object); 
     // Assert 
     Assert.That(fd.AddA("ABD"), Is.EqualTo("ABCAAA")); 
    } 
}