Comment utiliser des objets factices dans mes tests unitaires tout en utilisant la couverture de code?

StackOverflow https://stackoverflow.com/questions/336288

Question

Actuellement, je commence à introduire le concept d’objets simulés dans mes tests unitaires. En particulier, j'utilise le framework Moq. Cependant, une des choses que j'ai remarquées, c'est que les classes que je teste à l'aide de ce framework affichent soudain une couverture de code de 0%.

Maintenant, je comprends que, puisque je me moque de la classe, il n’exécute pas la classe elle-même .... mais comment puis-je écrire ces tests et faire en sorte que la couverture de code renvoie des résultats précis? Dois-je écrire un ensemble de tests qui utilisent Mocks et un autre pour instancier directement la classe?

Peut-être que je fais quelque chose de mal sans m'en rendre compte?

Voici un exemple de moi qui essaie de tester unitairement une classe appelée "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!";
        }
    }
}

Est-ce que quelqu'un sait ce que je devrais faire différemment?

Était-ce utile?

La solution

Vous n'utilisez pas vos objets fictifs correctement. Lorsque vous utilisez des objets fictifs, vous voulez tester la façon dont votre code interagit avec d'autres objets sans utiliser réellement les objets réels. Voir le code ci-dessous:

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();
        }
    }

Il ne semble pas que cela serve à quoi que ce soit lorsque votre code renvoie simplement une chaîne sans aucune logique derrière celle-ci.

Le vrai pouvoir vient si vous la méthode GetSomeString () avez fait une logique qui peut changer le résultat de la chaîne de sortie en fonction du retour de IDependdance . GiveMeAString () , vous pouvez voir comment votre méthode gère les données incorrectes envoyées à partir de l'interface IDependdance .

Quelque chose comme:

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

Maintenant, si vous avez cette ligne dans votre test:

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

Qu'adviendra-t-il de votre méthode GetSomeString () ?

Autres conseils

La grosse erreur est de se moquer du Système sous test (SUT) , vous testez autre chose. Vous ne devez vous moquer que des dépendances SUT.

Je vous conseillerais de rester à l'écart des cadres moqueurs jusqu'à ce que vous compreniez les interactions qui se déroulent ici.

IMO, il est préférable d’apprendre avec les doubles de test créés manuellement, puis de passer à un cadre moqueur par la suite. Mon raisonnement:

  1. Les cadres moqueurs font abstraction de ce qui se passe réellement; Il est plus facile de saisir les interactions si vous devez créer explicitement vos dépendances, puis suivez les tests du débogueur.

  2. Il est facile de mal utiliser les frameworks. Si vous lancez le vôtre lorsque vous apprenez, vous aurez plus de chances de comprendre les différences entre différents types de tests en double. Si vous passez directement à un framework moqueur, il est facile d’utiliser des simulacres quand vous voulez des talons et vice-versa - il y a une grande différence.

Pensez-y de cette façon: la classe à l’essai est au centre des préoccupations. Vous créez une instance de celle-ci, appelez ses méthodes puis affirmez que le résultat est correct. Si la classe sous test a des dépendances (par exemple, quelque chose est requis dans le constructeur), vous répondez à ces dépendances en utilisant A: classes réelles ou B: tests en double.

La raison pour laquelle nous utilisons des doublons de test est qu’elle isole la classe sous test, ce qui signifie que vous pouvez exercer son code de manière plus contrôlée.

E.g. Si vous avez une classe contenant un objet réseau, vous ne pouvez pas tester les routines de traitement d'erreur de la classe propriétaire qui détectent les connexions inactives si vous êtes obligé d'utiliser un objet de connexion réseau concret. Au lieu de cela, vous injectez un faux objet de connexion et lui dites de lever une exception lorsque son "SendBytes" méthode est appelée.

I.e. Dans chaque test, les dépendances de la classe sous test sont créées spécifiquement pour exercer une partie de code particulière.

Cela fait beaucoup de sens. En gros, vous dites que je dois faire ce qui suit:

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);

Instituer essentiellement le SUT et n'utiliser que des simulations pour les classes requises par le SUT?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top