Configurer comme suit:
public interface IFoo
{
void Fizz();
}
[Test]
public void A()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz());
// stuff here
foo.Verify(x => x.Fizz(), Times.Never()); // currently this fails
}
En gros, j'aimerais entrer du code dans // stuff here
Pour faire passer la foo.Verify(x => x.Fizz(), Times.Never())
.
Et parce que cela constitue probablement un abus de test moq/unit, ma justification est que je puisse faire quelque chose comme ceci:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// reset the verification here
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
Fondamentalement, j'ai un objet d'état où un peu de travail (à la fois en termes de fabrication de divers objets fictifs et d'autres faffings) est nécessaire pour le pousser dans State1. Ensuite, je veux tester la transition de State1 à State2. Au lieu de dupliquer ou d'abstraire le code, je préfère simplement réutiliser le test State1, le pousser dans State2 et effectuer mes assertions - tout ce que je peux faire sauf les appels de vérification.
Je ne pense pas que vous puissiez réinitialiser une maquette comme celle-ci. Au lieu de cela, si vous savez que Fizz
doit être appelé une fois lors de la transition vers l'état 1, vous pouvez effectuer vos vérifications comme ceci:
objectUnderTest.DoStuffToPushIntoState1();
foo.Verify(x => x.Fizz(), Times.Once()); // or however many times you expect it to be called
objectUnderTest.DoStuffToPushIntoState2();
foo.Verify(x => x.Fizz(), Times.Once());
Cela dit, je créerais toujours deux tests distincts pour cela. En tant que deux tests, il est plus facile de voir si la transition vers l'état 1 échoue ou la transition vers l'état 2 échoue. De plus, lorsque testé ensemble comme ceci, si votre transition vers l'état 1 échoue, la méthode de test se termine et votre transition vers l'état 2 n'est pas testée.
Modifier
À titre d'exemple, j'ai testé le code suivant avec xUnit:
[Fact]
public void Test()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed After State 1");
// stuff here
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed after State 2");
}
Ce test échoue avec le message "Échec après l'état 2". Cela simule ce qui se passerait si votre méthode qui pousse foo dans l'état 2 appelle Fizz
. Si c'est le cas, le second Verify
échouera.
En regardant à nouveau votre code, puisque vous appelez une méthode pour vérifier qu'elle n'appelle/n'appelle pas une autre méthode sur la maquette, je pense que vous devez définir CallBase
sur true
pour que la base DoStuffToPushIntoState2
est appelé plutôt que le remplacement de la maquette.
Je pense que longtemps après la création de ce message, ils ont ajouté la fonctionnalité demandée par l'OP, il existe une méthode d'extension Moq appelée Moq.MockExtensions.ResetCalls ().
Avec cette méthode, vous pouvez faire exactement ce que vous souhaitez, comme indiqué ci-dessous:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
foo.ResetCalls(); // *** Reset the verification here with this glorious method ***
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
Mise à jour
Maintenant, au lieu de .ResetCalls (), nous devrions utiliser .Invocations.Clear () sur la dernière version de la bibliothèque:
foo.Invocations.Clear()
J'ai également été témoin de l'échec de la vérification Times.Exactly (1) entre les tests unitaires utilisant MoQ, avec un message d'erreur "a été appelé 2 fois". Je vois cela comme un bug dans MoQ, car je m'attendrais à des états de simulation propres à chaque test.
Mon travail consistait à attribuer une nouvelle instance fictive et une cible de test dans la configuration de test.
private Mock<IEntityMapper> entityMapperMock;
private OverdraftReportMapper target;
[SetUp]
public void TestSetUp()
{
entityMapperMock = new Mock<IEntityMapper>();
target = new OverdraftReportMapper(entityMapperMock.Object);
}
Vous pouvez utiliser la méthode de rappel au lieu de vérifier et compter les appels.
Ceci est démontré sur la page de démarrage rapide Moq , donc:
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
Cela dépend de la version de Mock que vous utilisez, je sais que nous pouvons le faire
someMockObject.ResetCalls();
Il s'agit en effet d'un abus de test unitaire car vous vérifiez deux choses en un seul test. Votre vie serait beaucoup plus facile si vous supprimiez l'initialisation de ObjectUnderTest
du test pour une méthode de configuration commune. Vos tests deviennent alors beaucoup plus lisibles et indépendants les uns des autres.
Plus que le code de production, le code de test doit être optimisé pour la lisibilité et l'isolement. Un test pour un aspect du comportement du système ne doit pas affecter d'autres aspects. Il est vraiment beaucoup plus facile de refactoriser le code commun dans une méthode d'installation que d'essayer de réinitialiser les objets fictifs.
ObjectUnderTest _objectUnderTest;
[Setup] //Gets run before each test
public void Setup() {
var foo = new Mock<IFoo>(); //moq by default creates loose mocks
_objectUnderTest = new ObjectUnderTest(foo.Object);
}
[Test]
public void DoStuffToPushIntoState1ShouldCallFizz() {
_objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
}
[Test]
public void DoStuffToPushIntoState2ShouldntCallFizz() {
{
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
Ajout à la réponse de @stackunderflow (haha, Nice nick :))
Dans les versions ultérieures de Moq, Moq.MockExtensions.ResetCalls()
était marqué comme obsolète. mock.Invocations.Clear()
doit être utilisé à la place:
foo.Invocations.Clear();
La prochaine approche fonctionne bien pour moi (en utilisant Moq.Sequence)
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// Some cool stuff
using (Sequence.Create())
{
foo.Setup(x => x.Fizz()).InSequence(Times.Never())
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
}
}
Faites-moi savoir si cela a fonctionné pour vous