Devo usare una classe separata per test?

1

Seguendo il seguente semplice metodo, come suggeriresti di scrivere un unit test per questo (sto usando MSTest ma i concetti sono simili in altri strumenti).

public void MyMethod(MyObject myObj, bool validInput)
{    
 if(!validInput)
 {
  // Do nothing
 }
 else
 {
  // Update the object
  myObj.CurrentDateTime = DateTime.Now;
  myObj.Name = "Hello World";
 }
}

Se provo a seguire la regola di un assert per test, la mia logica sarebbe che dovrei avere un metodo Initialise di classe che esegue il metodo e quindi test individuali che controllano ogni proprietà su myobj.

public class void MyTest
{
    MyObj myObj;

    [TestInitialize]
    public void MyTestInitialize()
    {
        this.myObj = new MyObj();
        MyMethod(myObj, true);
    }

    [TestMethod]
    public void IsValidName()
    {
        Assert.AreEqual("Hello World", this.myObj.Name);
    }

    [TestMethod]
    public void IsDateNotNull()
    {
        Assert.IsNotNull(this.myObj.CurrentDateTime);
    }
}

Dove sono confuso è intorno al TestInitialize . Se eseguo il metodo in TestInitialize , avrei bisogno di classi separate per variazione degli input dei parametri. È corretto? Questo mi lascerebbe con un numero enorme di file nel mio progetto (a meno che non abbia più classi per file).

    
posta user460667 04.04.2012 - 12:15
fonte

2 risposte

10

Devi guardare il pattern "Arrange Act Assert" .

Per ogni test tu:

Disponi il codice sotto test e qualsiasi variabile dipendente.
Agisci chiamando il metodo sul codice sotto test.
Assert ciò di cui hai bisogno per garantire il test passa (questo dovrebbe essere una cosa per test).

In questo caso utilizzerai:

[TestMethod]
public void MyMethod_CalledWithValidName_HasHelloWorldName()
{
    //Arrange
    bool isValid = true;
    MyObj objectParameter = new MyObj();
    ClassUnderTest objectUnderTest = new ClassUnderTest();

    //Act
    objectUnderTest.MyMethod(objectParameter, isValid);

    //Assert
    string expectedName = "Hello World";
    string actualName = this.myObj.Name;
    Assert.AreEqual(expectedName, actualName);
}

Questo ti consente di vedere esattamente cosa stai testando e copiando questo test e cambiando le variabili, scrivi rapidamente test aggiuntivi per altre condizioni.

Nota il modello di denominazione MethodUnderTest_Condition_ExpectedResult per il test.

Per rispondere effettivamente alla tua domanda:

If I execute the method under TestInitialize, I would need seperate classes per variation of parameter inputs.

Perché? TestInitialize consiste nell'impostare l'ambiente necessario per il test ogni in quella classe ed eseguire prima di ogni test. Nella mia esperienza questi tendono ad essere relativamente piccoli poiché i diversi metodi che si stanno testando hanno dipendenze diverse e dovrebbero essere abbastanza isolati da non dover impostare troppo lo stesso per ogni test. Se è necessario variare gli input dei parametri per ciascun test, allora non appartiene a TestInitialize, appartiene alla parte Arrange del test.

In questa classe hai un test contro un metodo su una classe non specificata (MyMethod) e un test contro myObj. Questi test dovrebbero essere in classi diverse.

Stai confondendo TestInitalize (setup prima di ogni test) come ClassInitialize (setup prima di tutti i test in quella classe).

ClassInitialize può essere abusato e può rendere importante l'ordine dei test. Un test non dovrebbe dipendere da un altro test. Puoi terminare con passaggi / errori in base all'ordine di esecuzione del test, che in alcuni casi non può essere specificato e causare interruzioni quando i test vengono aggiornati o rimossi o eseguiti in isolamento.

Il modo per assicurarsi che myObj.CurrentDateTime non sia nullo quando si prova la classe non specificata è di impostarlo, non si asserirà nulla su quel myObj in quei test. Puoi affermare contro di loro nella classe di test per myObj. Una classe di test per oggetto con un assert per test.

Sembra anche che MyMethod sia statico. Questo interrompe l'isolamento del test, nel senso che quando il codice in prova viene eseguito funziona con un altro codice di produzione. Questo trasforma questo test in un test di integrazione (poiché il codice sotto test e la classe statica vengono entrambi eseguiti durante il test); questo è negativo perché un errore nella classe statica può causare errori nel codice sotto test in metodi perfettamente funzionanti.

Ho coperto molti aspetti diversi dei test in questa risposta sulla base di molti principi diversi. Prendi il fantastico The Art Of Unit Testing e leggerlo copertinalmente. È una lettura fantastica e di facile lettura ed è uno dei libri che vorrei prendere (insieme al codice completo) se l'ufficio prendesse fuoco.

    
risposta data 04.04.2012 - 12:25
fonte
1

dovresti chiamare il metodo che testerai in TestMethod . la funzione TestInitialize imposta tutto per evitare la duplicazione del codice: viene chiamata ogni volta prima di una funzione TestMethod in modo da iniziare da una lavagna vuota

public class void MyTest {
     MyObj myObj;
      [TestInitialize]
     public void MyTestInitialize()
     {
         this.myObj = new MyObj();

      }

     [TestMethod]
     public void IsValidName()
     {
         MyMethod(myObj, true);
         Assert.AreEqual("Hello World", this.myObj.Name);
     }

     [TestMethod]
     public void IsInvalidName()
     {
         MyMethod(myObj, false);
         Assert.AreNotEqual("Hello World", this.myObj.Name);
     }



} 
    
risposta data 04.04.2012 - 12:21
fonte

Leggi altre domande sui tag