Implémentation d'objets moqueurs avec Moq lorsque le constructeur a des paramètres
J'ai lu ce réponse par Ragzitsu pour la même question. Je suis encore confus comment mettre en œuvre des choses bien. Quelqu'un peut-il me donner un exemple de mise en œuvre.
J'ai les classes suivantes:
class Fizz : IFizz
{
}
class Buzz : IBuzz
{
}
class Bar : IBar
{
}
class Foo : IFoo
{
public Foo(IBar bar, IFizz fizz, IBuzz buzz)
{
//initialize etc.
}
//public methods
}
Quel est le moyen le plus pratique pour contourner le constructeur ici? Je veux faire quelque chose comme
var foo = new Mock<IFoo>();
En d'autres termes comment le code s'occuper de l'avis
The best thing to do would be right click on your class and choose Extract interface.
source d'informationauteur happygilmore | 2013-12-22
Vous devez vous connecter pour publier un commentaire.
Vous pouvez créer des maquettes où le constructeur a param arguments, en se référant MockBehavior, illustré ci-dessous
Je vais traiter ce concept dans un sens orthogonal. Je suis en désaccord avec cette déclaration, une interface n'est pas la solution à la situtation en question.
Revenir à la question précédente du texte:
Remarque la méthode que j'ai ajoutée.
Je crois que la vraie question, c'est quand vous n'êtes pas à tester
CustomerSyncEngine
mais plutôt de tester une classe qui dépend surCustomerSyncEngine
. Appelons cette classeSuperSyncEngine
. La création d'un test contreSuperSyncEngine
va être une douleur depuis que vous avez à se moquer de l'ensemble de laCustomerSyncEngine
avec ses 3 interfaces, ainsi que toutes les autres dépendances supplémentairesSuperSyncEngine
a.Étant donné que le code que vous cherchez à tester est
SuperSyncEngine
qui dépend de laCustomerSyncEngine
une interface n'est pas la réponse ici. Vous pourrait créerICustomerSyncEngine
mais que l'interface ne devrait pas être créé simplement pour se moquant de cadre. La meilleure solution est de changerCustomerSyncEngine.MethodWithDependencies
être virtuelCela vous permettra de remplacer la méthode avec un moqueur cadre d'ignorer les dépendances qui
CustomerSyncEngine
qui vient avec.Si vous suivez cette approche, vous aurez probablement besoin d'un constructeur par défaut exposés sur
CustomerSyncEngine
pour lui permettre d'être moqué. Il est possible de contourner cela et satisfaire les dépendances avec la valeur null ou certains autres valeurs, mais que serait le travail supplémentaire lorsque l'objectif est de réduire le frottement.Vous ne devriez pas changer quoi que ce soit si vous disposez d'une interface
IFoo
et veulent se moquer deFoo
classe qui a constructeur avec paramètres.Votre code est exactement ce dont vous avez besoin.
Les conseils suivants couvre les situations où la classe n'a pas d'interface et pas de constructeur sans paramètre. En fait, vous pouvez passer tous les paramètres nécessaires dans le constructeur de
Mock
.mais
Je sais que cela a été demandé il y a un moment et peut-être n'a pas de valeur pour moi d'ajouter à cela maintenant, mais pour les futurs lecteurs, il ne faut pas se laisser prendre sur la pensée que sont la même chose lors de l'utilisation moq:
n'est pas la même chose que:
Ma compréhension est que lors de la création d'une Maquette de IFoo, le moq cadre va générer une double classe qui implémente l'IFoo interface, permettant ainsi au constructeur par défaut pour elle-même dans l'généré la mise en œuvre. La maquette de Foo bon, toutefois, de ne pas mettre en œuvre l'interface directement dans le code généré, mais plutôt hérite de la de béton classe Foo et nécessite donc des méthodes qui doivent être écrasé être virtuelles, et si il y a de tout des implémentations concrètes que vous souhaitez réellement utiliser (l'unité sous test, par exemple), alors vous avez besoin "CallBase = true" sur votre maquette. Si c'est ce que vous cherchez à faire avec une classe concrète qui a des arguments du constructeur, vous cherchez peut-être pour @Palkin de réponse afin d'éviter d'exposer un constructeur par défaut.
Comme une modification à fait répondre à la question de
Les conseils pour en extraire une interface peut ne pas être exactement ce que vous cherchez. Comme il a été mentionné dans d'autres réponses, nous sommes pas sûr de ce que vous êtes réellement en train de tester,.
Si vous essayez de tester fonctionnalité sur le béton
Foo
mise en œuvre deIFoo
puis l'extraction d'une interface et se moquant deIFoo
n'est pas vraiment vous aider, car moq va mettre en œuvre l'interface elle-même et fournir seulement les "fonctionnalités" vous dites de lui fournir, dans le programme d'installation méthodes et les propriétés de mise en œuvre. Si c'est ce que vous faites, ET la méthode dans les appels de test d'une autre méthode sur le bétonFoo
mise en œuvre, ET que la méthode est quelque chose que vous voulez fournir une abstraction, alors je ferais comme @Chris Marisic mentionné et de modifier la méthode que vous avez le désir de l'abstrait auvirtual
. Avec ce que le virtuel vous peut utiliser un moqueur cadre de l'abstraction, ou faire comme j'ai fait dans le passé et de créer un sous-types de l'objet sous test dans la classe de test.Avec cette méthode, vous allez encore besoin de fournir quelque chose pour le constructeur de Toto, mais ce serait un premier cas d'utilisation se moque sur ces interfaces.
Maintenant, si vous testez
Foo
et la méthode de test est juste l'appel de méthodes surIBar
IFizz
IBuzz
et il n'y a pas d'autre abstraction que vous voulez créer, alors vous êtes déjà en place. Se moquer de ceux interfaces, les mettre en place pour rendre ce que vous attendez dans un cas de test, et de fournirFoo
avec les objets fantaisie.L'extrait de l'interface des conseils, c'est vraiment utile lorsque votre objet sous test dépend de Foo, comme mentionné par @Chris Marisic. Cependant, je ne partage pas son sentiment de ne pas vouloir créer des interfaces dans cette situation. Cela dépend de ce que sent de plus pour vous: création d'une interface pour fournir la maquette de capacité, ou de changer le modificateur de fournir le même. Si vous allez changer le modificateur et toujours utiliser moq pour se moquer de béton
Foo
vous aurez besoin d'exposer un constructeur par défaut (assez malodorante pour moi) ou la création de la maquette avec les arguments du constructeur de spécification, comme mentionné dans votre référencé répondre.Permet de dire que vous avez les éléments suivants:
Ci-dessus
Baz
dépend deFoo
. Après l'extraction d'une interface deFoo
Baz
devraient accepterIFoo
.Puis
Foo
aurait l'air comme il le fait dans votre question etIFoo
aurait une définition de la signature de la méthode que vous voulez faire abstraction deBaz
.Maintenant, dans votre test, vous devrez utiliser
var foo = new Mock<IFoo>();
et donnerfoo
à votreBaz
sujet de test. Ne pas oublier de configurer leIFoo.GetStuff()
méthode.Parce que vous avez maintenant créé le
IFoo
l'abstraction n'est pas nécessaire deparce que le simulacre de
IFoo
seulement a un constructeur par défaut fournis par moq.Personnellement, je préfère l'approche de l'interface lorsqu'elle s'applique, car elle élimine
IFizz
IBuzz
etIBar
de la chaîne de dépendances surBaz
. SiBaz
dépendFoo
etFoo
dépendIFizz etc.
puisBaz
dépend aussi deIFizz etc.
Après l'extraction deIFoo
Baz
dépendIFoo
seulement.C'est un vieux post mais je viens de tomber sur un problème similaire (en commençant avec Moq). Dans le cas où quelqu'un d'autre a le même problème, ici, c'est ce que j'avais:
Ce que je suis en train de faire est de tester
Manager
pasFoo
.Voici mon premier test de code qui a jeté une Erreur.
L'erreur est
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Something.Models.Foo. Could not find a parameterless constructor.
La lecture du message d'Erreur, Moq ne comprends pas comment instancier Foo, car il n'y a pas de constructeur sans paramètre, et nous ne disons pas Moq comment instancier un avec des paramètres. Changement deuxième section:
Si vous allez à des tests de votre classe FOo, vous n'avez pas besoin de se moquer. Vous avez seulement besoin de se moquer de ceux des classes qui dépend de la classe que vous essayez de tests.
Quelque chose comme:
Puis appeler la méthode foo que vous souhaitez tester 😉
Si la méthode foo utilise une méthode à l'intérieur d'un bar/fuzz ou fizz, alors vous devriez utiliser le sintax comme:
De cette façon, lorsque votre méthode foo est appelé, il va appeler le DoSomething et toujours sera de retour 1 😉
Interfaces sont utiles quand un type a plus d'une mise en œuvre. Côté négatif, cela signifie que vous devez prendre soin de ne pas dériver interfaces juste parce que vous voulez interfaces (erreur fréquente). Sur un côté positif, se moquaient de l'interface est la deuxième mise en œuvre, par définition.
Donc, la conclusion est que si un type d'actes comme un autre type de dépendance, alors il est un bon candidat pour implémenter une interface. Dans ce cas, vous serez en mesure de se moquer de l'interface dans les tests unitaires librement et pleinement.
Sur une note connexe, lors de la définition d'une interface, assurez-vous de n'ajouter que des parties fonctionnelles de l'interface: des méthodes qui définissent ce que l'objet nepas ce qu'il ressemble. Avoir une interface avec un tas de getters/setters ne pas ajouter de la valeur à la conception. C'est assez grand domaine de la théorie, et cette minuscule fenêtre n'est pas l'endroit pour écrire plus à ce sujet.
De clarifier la connexion avec votre question: est moqué de la mise en œuvre devrait prévoir le comportement requis par l'interface. Pour ce faire, vous utilisez les fonctionnalités de la dérision cadre. Cela n'a rien à voir avec la mise en œuvre concrète de la classe Foo - vous définir le comportement spécifique de l'objet Fantaisie.