web-dev-qa-db-fra.com

Moq, utilisation stricte vs utilisation lâche

Dans le passé, je n'ai utilisé que des Rhino Mocks, avec la maquette stricte typique. Je travaille maintenant avec Moq sur un projet et je m'interroge sur la bonne utilisation.

Supposons que j'ai un objet Foo avec la méthode Bar qui appelle une méthode Bizz sur l'objet Buzz.

Dans mon test, je veux vérifier que Bizz est appelé, donc je pense qu'il y a deux options possibles:

Avec une maquette stricte

var mockBuzz= new Mock<IBuzz>(MockBehavior.Strict);
mockBuzz.Setup(x => x.Bizz()); //test will fail if Bizz method not called
foo.Buzz = mockBuzz
foo.Bar();
mockBuzz.VerifyAll();

Avec une maquette lâche

var mockBuzz= new Mock<IBuzz>();    
foo.Buzz = mockBuzz
foo.Bar();
mockBuzz.Verify(x => x.Bizz()) //test will fail if Bizz method not called

Existe-t-il une manière standard ou normale de procéder?

51
Bryan Rowe

J'avais l'habitude d'utiliser des simulations strictes lorsque j'ai commencé à utiliser des simulations dans des tests unitaires. Cela n'a pas duré très longtemps. Il y a vraiment 2 raisons pour lesquelles j'ai arrêté de faire ça:

  1. Les tests deviennent fragiles - Avec des simulations strictes, vous affirmez plus d'une chose, que les méthodes de configuration sont appelées ET que les autres méthodes ne sont pas appelées. Lorsque vous refactorisez le code, le test échoue souvent, même si ce que vous essayez de tester est toujours vrai.
  2. Les tests sont plus difficiles à lire - Vous devez avoir une configuration pour chaque méthode appelée sur la maquette, même si elle n'est pas vraiment liée à ce que vous voulez tester. Lorsque quelqu'un lit ce test, il lui est difficile de dire ce qui est important pour le test et ce qui n'est qu'un effet secondaire de la mise en œuvre.

Pour cette raison, je recommanderais fortement d'utiliser des simulations lâches dans vos tests unitaires.

77
Andy Lowry

J'ai une formation en développement C++/non.NET et j'ai été plus intéressé par .NET récemment, donc j'avais certaines attentes lorsque j'utilisais Moq pour la première fois. J'essayais de comprendre que WTF poursuivait mon test et pourquoi le code que je testais lançait une exception aléatoire au lieu de la bibliothèque Mock me disant quelle fonction le code essayait d'appeler. J'ai donc découvert que je devais activer le comportement strict, ce qui était perplexe, puis je suis tombé sur cette question à laquelle je n'ai pas encore répondu.

Le mode Lâche , et le fait qu'il s'agit du mode par défaut est fou . À quoi sert une bibliothèque Mock qui fait quelque chose de complètement imprévisible que vous n'avez pas explicitement indiqué qu'elle devrait faire?

Je suis totalement en désaccord avec les points énumérés dans les autres réponses à l'appui du mode Lâche. Il n'y a aucune bonne raison de l'utiliser et je ne le souhaiterais jamais. Lors de l'écriture d'un test unitaire, je veux être certain de ce qui se passe - si je sais qu'une fonction doit retourner un null, je le ferai retourner cela. Je veux que mes tests soient fragiles (de la manière qui compte) pour que je puisse les corriger et ajouter à la suite de codes de test les lignes de configuration qui sont les informations explicites qui me décrivent exactement ce que mon logiciel fera.

La question est - existe-t-il une manière standard et normale de procéder?

Oui - du point de vue de la programmation en général, c'est-à-dire d'autres langages et en dehors du monde .NET, vous devez toujours utiliser Strict. Dieu sait pourquoi ce n'est pas la valeur par défaut dans Moq.

15
Benedict

J'ai une convention simple:

  1. Utilisez des simulations strictes lorsque le système testé (SUT) délègue l'appel à la couche simulée sous-jacente sans vraiment modifier ou appliquer de logique métier aux arguments transmis à lui-même.

  2. Utilisez des simulations lâches lorsque le SUT applique la logique métier aux arguments passés à lui-même et transmet certaines valeurs dérivées/modifiées à la couche simulée.

Par exemple: Disons que nous avons le fournisseur de base de données StudentDAL qui a deux méthodes:

L'interface d'accès aux données ressemble à quelque chose comme ci-dessous:

public Student GetStudentById(int id);
public IList<Student> GetStudents(int ageFilter, int classId);

L'implémentation qui consomme ce DAL ressemble à ci-dessous:

public Student FindStudent(int id)
{
   //StudentDAL dependency injected
   return StudentDAL.GetStudentById(id);
   //Use strict mock to test this
}
public IList<Student> GetStudentsForClass(StudentListRequest studentListRequest)
{
  //StudentDAL dependency injected
  //age filter is derived from the request and then passed on to the underlying layer
  int ageFilter = DateTime.Now.Year - studentListRequest.DateOfBirthFilter.Year;
  return StudentDAL.GetStudents(ageFilter , studentListRequest.ClassId)
  //Use loose mock and use verify api of MOQ to make sure that the age filter is correctly passed on.

}
9
Amol

Personnellement, étant nouveau dans la moquerie et Moq, je pense que commencer avec le mode Strict aide à mieux comprendre les entrailles et ce qui se passe. "Loose" cache parfois des détails et passe un test qu'un débutant en moq peut ne pas voir. Une fois que vous avez vos compétences moqueuses - Loose serait probablement beaucoup plus productif - comme dans ce cas, enregistrer une ligne avec le "Setup" et utiliser simplement "Verify" à la place.

4
mithun_daa