Domanda

Attualmente sto iniziando a introdurre il concetto di oggetti simulati nei miei test di unità. In particolare sto usando il framework Moq. Tuttavia, una delle cose che ho notato è che improvvisamente le classi che sto testando usando questo framework mostrano una copertura del codice dello 0%.

Ora capisco che dal momento che sto solo deridendo la classe, non sta eseguendo la classe vera e propria .... ma come faccio a scrivere questi test e fare in modo che la copertura del codice restituisca risultati accurati? Devo scrivere un set di test che utilizzano Mock e un set per istanziare direttamente la classe.

Forse sto facendo qualcosa di sbagliato senza accorgermene?

Ecco un esempio del mio tentativo di Unit Test di una classe chiamata " MyClass " ;:

using Moq;
using NUnitFramework;

namespace MyNameSpace
{
    [TestFixture]
    public class MyClassTests
    {

        [Test]
        public void TestGetSomeString()
        {
            const string EXPECTED_STRING = "Some String!";

            Mock<MyClass> myMock = new Mock<MyClass>();
            myMock.Expect(m => m.GetSomeString()).Returns(EXPECTED_STRING);

            string someString = myMock.Object.GetSomeString();

            Assert.AreEqual(EXPECTED_STRING, someString);
            myMock.VerifyAll();

        }

    }

    public class MyClass
    {
        public virtual string GetSomeString()
        {
            return "Hello World!";
        }
    }
}

Qualcuno sa cosa dovrei fare diversamente?

È stato utile?

Soluzione

Non stai usando correttamente i tuoi oggetti finti. Quando si utilizzano oggetti finti, si intendeva testare il modo in cui il codice interagisce con altri oggetti senza effettivamente utilizzare gli oggetti reali. Vedi il codice qui sotto:

using Moq;
using NUnitFramework;

namespace MyNameSpace
    {
        [TestFixture]
        public class MyClassTests
        {

            [Test]
            public void TestGetSomeString()
            {
                const string EXPECTED_STRING = "Some String!";

                Mock<IDependance> myMock = new Mock<IDependance>();
                myMock.Expect(m => m.GiveMeAString()).Returns("Hello World");

                MyClass myobject = new MyClass();

                string someString = myobject.GetSomeString(myMock.Object);

                Assert.AreEqual(EXPECTED_STRING, someString);
                myMock.VerifyAll();

            }

        }

        public class MyClass
        {

            public virtual string GetSomeString(IDependance objectThatITalkTo)
            {
                return objectThatITalkTo.GiveMeAString();
            }
        }

        public interface IDependance
        {
            string GiveMeAString();
        }
    }

Non sembra che stia facendo qualcosa di utile quando il tuo codice sta semplicemente restituendo una stringa senza alcuna logica.

Il vero potere arriva se il tuo metodo GetSomeString () ha fatto qualche logica che potrebbe cambiare il risultato della stringa di output in base al ritorno dal IDependdance . GiveMeAString () , quindi puoi vedere come il tuo metodo gestisce i dati errati inviati dall'interfaccia IDependdance .

Qualcosa del tipo:

 public virtual string GetSomeString(IDependance objectThatITalkTo {
     if (objectThatITalkTo.GiveMeAString() == "Hello World")
     return "Hi";
 }

Ora se hai questa linea nel tuo test:

myMock.Expect(m => m.GiveMeAString()).Returns(null);

Cosa accadrà al tuo metodo GetSomeString () ?

Altri suggerimenti

Un grosso errore sta prendendo in giro il System Under Test (SUT) , prova qualcos'altro. Dovresti deridere solo le dipendenze SUT.

Consiglierei di stare alla larga dai framework beffardi fino a quando non capirai le interazioni che stanno avvenendo qui.

IMO è meglio imparare con i doppi di test creati manualmente, per poi passare a un quadro beffardo in seguito. Il mio ragionamento:

  1. I framework beffardi sottraggono ciò che sta realmente accadendo; è più facile cogliere le interazioni se devi creare esplicitamente le tue dipendenze, quindi seguire i test nel debugger.

  2. È facile abusare dei framework. Se ottieni il tuo risultato mentre stai imparando, è più probabile che tu comprenda le differenze tra i diversi tipi di test doppi. Se vai direttamente a un quadro beffardo, è facile usare beffe quando volevi stub e viceversa, c'è una grande differenza.

Pensaci in questo modo: la classe sotto test è al centro dell'attenzione. Ne crei un'istanza, ne chiami i metodi e poi asserisci che il risultato è corretto. Se la classe sotto test ha dipendenze (ad es. Qualcosa è richiesto nel costruttore), si soddisfano tali dipendenze usando A: classi reali o B: test doppie.

Il motivo per cui usiamo test double è che isola la classe sotto test, il che significa che puoi esercitare il suo codice in modo più controllato.

es. se si dispone di una classe che contiene un oggetto di rete, non è possibile verificare le routine di gestione degli errori della classe proprietaria che rilevano connessioni morte se si è costretti a utilizzare un oggetto di connessione di rete concreto. Invece, si inietta un oggetto di connessione falso e gli dice di lanciare un'eccezione quando i suoi "quotBytes" viene chiamato metodo.

vale a dire. In ogni test, le dipendenze della classe sotto test sono create appositamente per esercitare un particolare pezzo di codice.

Questo ha molto senso. In sostanza stai dicendo che devo fare quanto segue:

public class MyClass
{
    public virtual string GetSomeString(MyOtherClass moc)
    {
        return moc.ToString();
    }
}

.....

Mock<MyOtherClass> myMock = new Mock<MyOtherClass>();

MyClass mc = new MyClass();

string someString = mc.GetSomeString(myMock.Object);
Assert.AreEqual(EXPECTED_STRING, someString);

Essenzialmente istanziando il SUT e usando solo beffe per le classi richieste dal SUT?

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