• 12 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 16/12/2019

Utilisez le framework de simulacre Moq

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Après avoir créé de faux objets manuellement, nous allons maintenant voir comment le faire grâce à un framework de simulacre.

Moq est un framework de simulacre qui va nous permettre de créer de faux objets facilement. Facile à mettre en place, simple d’utilisation, c’est l’outil qu’il nous faut.

Installer Moq via Nuget
Installer Moq via Nuget

Nos tests ne compilent plus, et pour cause, il nous manque une dépendance dans la classe  Jeu . Pour créer cette fausse dépendance avec Moq, il suffit de faire :

// Arrange
var fournisseurMeteo = Mock.Of<IFournisseurMeteo>();
Jeu jeu = new Jeu(fournisseurMeteo);
// Act
var resultat = jeu.Tour(6, 1);
// Assert
resultat.Should().Be(Resultat.Gagne);
jeu.Heros.Points.Should().Be(1);
jeu.Heros.PointDeVies.Should().Be(15);

Moq est capable de créer de fausses implémentations à partir d’une abstraction (interface ou classe abstraite avec le mot-clé  virtual ). Il suffit de l’utiliser avec la méthode  Mock.Of<> . Dynamiquement, Moq crée une classe proxy et nous permettra d’utiliser cet objet comme si c’était le vrai.

Dans notre cas de test,  fournisseurMeteo n’est jamais utilisé, car il ne sert que lorsque le joueur a un lancer de dé inférieur à celui du monstre. Donc nous ne voyons pas directement l’effet de ce faux objet (à part qu’il a résolu nos problèmes de compilation).

Mais voyons ce qu’il se passe dans le cas du dernier test :

[TestMethod]
[Description("Etant donné un tour de jeu, lorsque j'ai un lancer inférieur au second, alors le résultat est perdu, sans point et en perdant des points de vie")]
public void Tour_AvecUnDeInferieurAuSecond_RetournePerduSansPointEnPerdantDesPointsDeVie()
{
// Arrange
var fournisseurMeteo = Mock.Of<IFournisseurMeteo>();
Jeu jeu = new Jeu(fournisseurMeteo);
// Act
var resultat = jeu.Tour(2, 4);
// Assert
resultat.Should().Be(Resultat.Perdu);
jeu.Heros.Points.Should().Be(0);
jeu.Heros.PointDeVies.Should().Be(13);
}

Si j’exécute le test, il passe.

Mais quelle donnée de météo a donc été utilisée ?

Pour le savoir, vous pouvez aller voir en debug ce qu’il se passe. Nous pouvons notamment :

  • voir que l’objet  _fournisseurMeteo  est d’un type bizarre, il s’agit d’un proxy ;

Moq génère un proxy au runtime
Moq génère un proxy au runtime
  • que la valeur renvoyée est  Meteo.Soleil .

La météo est renvoyée par l'objet Mock
La météo est renvoyée par l'objet Mock

Alors pourquoi  Meteo.Soleil  ? Tout simplement parce que nous n’avons pas défini de valeur à utiliser ; alors il prend la valeur par défaut, en l’occurrence la première valeur de notre énumération.

Pour lui faire prendre une valeur spécifique, il va falloir le lui indiquer :

[TestMethod]
[Description("Etant donné un tour de jeu, lorsque j'ai un lancer inférieur au second et du vent, alors le résultat est perdu, sans points et en perdant deux fois plus de points de vie")]
public void Tour_AvecUnDeInferieurAuSecond_EtDuVent_RetournePerduSansPointEnPerdantDeuxFoisPlusDePointsDeVie()
{
// Arrange
var fournisseurMeteo = Mock.Of<IFournisseurMeteo>();
Mock.Get(fournisseurMeteo).Setup(m => m.QuelTempsFaitIl()).Returns(Meteo.Tempete);
Jeu jeu = new Jeu(fournisseurMeteo);
// Act
var resultat = jeu.Tour(2, 4);
// Assert
resultat.Should().Be(Resultat.Perdu);
jeu.Heros.Points.Should().Be(0);
jeu.Heros.PointDeVies.Should().Be(11);
}

 
C’est la ligne 7 qui indique que la méthode  QuelTempsFaitIl  du faux objet de météo va renvoyer  Meteo.Tempête , en se moquant bien du générateur aléatoire. On peut le vérifier avec nos assertions de fin de tests : les dégâts doivent avoir doublé, donc avoir un nombre de points de vie égal à 11.

N’hésitez pas à créer le test qui vérifie ce qu’il se passe avec de la pluie. Il suffit d’utiliser :

Mock.Get(fournisseurMeteo).Setup(m => m.QuelTempsFaitIl()).Returns(Meteo.Pluie);

Vérifier que le résultat vaut à nouveau 13. En effet, nous n’avons pas de malus lorsqu’il y a de la pluie.

D’autres exemples de Moq

Moq nous permet de faire plein d’autres choses qui peuvent nous servir régulièrement dans nos tests.

Nous avons vu que nous pouvions simuler le résultat d’une méthode et forcer une valeur spécifique. Cela fonctionne lorsqu’il n’y a pas de paramètre à la méthode (comme dans notre précédent exemple), mais aussi lorsqu’il y en a.

Nous pouvons même faire prendre une valeur spécifique en fonction de la valeur du paramètre passé.

Par exemple, en considérant la classe de démonstration suivante :

public class Demo
{
public virtual int DemoMethode(int valeur)
{
return 1;
}
}

Avec les arrangements suivants, j'ai les résultats ci-dessous :

[TestMethod]
public void Moq_Exemples()
{
var fauxObjet = Mock.Of<Demo>();
Mock.Get(fauxObjet).Setup(x => x.DemoMethode(1)).Returns(4);
Mock.Get(fauxObjet).Setup(x => x.DemoMethode(6)).Returns(0);
fauxObjet.DemoMethode(6).Should().Be(0);
fauxObjet.DemoMethode(1).Should().Be(4);
fauxObjet.DemoMethode(6).Should().Be(0);
}

J’ai fait exprès de mettre la méthode avec le paramètre 6 deux fois pour montrer que l’ordre n’a pas d’influence sur le comportement.

Si la valeur du paramètre n’a pas d’intérêt, on peut utiliser la méthode  It.IsAny<int>()  pour renvoyer un résultat, peu importe le paramètre de type entier qui est passé :

[TestMethod]
public void Moq_Exemples()
{
var fauxObjet = Mock.Of<Demo>();
Mock.Get(fauxObjet).Setup(x => x.DemoMethode(It.IsAny<int>())).Returns(4);
fauxObjet.DemoMethode(0).Should().Be(4);
fauxObjet.DemoMethode(1).Should().Be(4);
fauxObjet.DemoMethode(-16).Should().Be(4);
}

On peut aussi faire en sorte que les valeurs changent au fur et à mesure des appels, en utilisant  SetupSequence  :

[TestMethod]
public void Moq_Exemples()
{
var fauxObjet = Mock.Of<Demo>();
Mock.Get(fauxObjet).SetupSequence(x => x.DemoMethode(It.IsAny<int>()))
.Returns(4)
.Returns(5)
.Returns(6);
fauxObjet.DemoMethode(0).Should().Be(4);
fauxObjet.DemoMethode(1).Should().Be(5);
fauxObjet.DemoMethode(-16).Should().Be(6);
}

Au premier appel, la méthode va retourner 4. Puis au second appel 5, et ensuite 6.

Ce qui veut dire que nous allons être capables de nous passer de la classe  FauxDe  que nous avions écrite en utilisant :

[TestMethod]
public void Ihm_AvecUnJeuDeDonnees_LeJoueurGagne()
{
// arrange
var fausseConsole = new FausseConsole();
var fauxDe = Mock.Of<ILanceurDeDe>();
var sequence = Mock.Get(fauxDe).SetupSequence(de => de.Lance());
foreach (var lancer in new[] { 4, 5, 1, 1, 4, 3, 5, 6, 6, 6, 1, 2, 4, 2, 3, 2, 6, 4, 5, 1, 1, 4, 3, 5, 6, 6, 6, 1, 2, 4, 2, 3, 2, 6 })
{
sequence.Returns(lancer);
}
var fournisseurMeteo = Mock.Of<IFournisseurMeteo>();
var ihm = new Ihm(fausseConsole, fauxDe, fournisseurMeteo);
// act
ihm.Demarre();
// assert
var resultat = fausseConsole.StringBuilder.ToString();
resultat.Should().StartWith("A l'attaque : points/vie 0/15");
resultat.Should().EndWith("Combat perdu: points/vie 9/0\r\n");
resultat.Should().HaveLength(560);
}

Ici, la variable  fauxDe  a le même comportement que si l'on avait utilisé la classe  FauxDe , sauf que nous avons utilisé Moq pour lui fournir une liste de valeurs prédéfinies.

Une méthode simulée peut également lever une exception :

[TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void Moq_Exemples()
{
var fauxObjet = Mock.Of<Demo>();
Mock.Get(fauxObjet).Setup(x => x.DemoMethode(It.IsAny<int>()))
.Throws<NotImplementedException>();
fauxObjet.DemoMethode(1);
}

Moq peut faire encore beaucoup de choses, par exemple bouchonner des propriétés, ou vérifier que des méthodes ont été appelées. N’hésitez pas à consulter la documentation à ce sujet.

Résumé de la partie

Vous avez maintenant une meilleure vision de ce que sont les simulacres et les bouchons. Ils sont des éléments indispensables dès que nous avons besoin de maîtriser une dépendance imprévisible. Sans cela, les tests ont de fortes chances de renvoyer des faux positifs plus ou moins rapidement (en général, très rapidement lorsqu’il s’agit d’aléatoire !). Vous pouvez écrire vous-même vos faux objets ou bien utiliser simplement des frameworks de simulacres.

Exemple de certificat de réussite
Exemple de certificat de réussite