Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq - Is it possible to specify in a Setup the Verify criteria (e.g. Times called)?

If you need to Setup a return value, as well as Verify how many times the expression was called, can you do this in one statement?

From what I can gather, Moq's Setup(SomeExpression).Verifiable() called along with Verify(), basically does a Verify(SomeExpression, Times.AtLeastOnce)? i.e. it verifys the expression was called only.

Here's an example to explain the question better. For an interface:

interface IFoo {     int ReturnSomething(); } 

Are the following two blocks equivalent (other than the first will Verify all setups marked as verifiable)?

void Test() {     var mock = new Mock<IFoo>();     mock.Setup((m) => m.ReturnSomething()).Returns(1).Verifiable();      mock.Verify(); } 

and

void Test() {     var mock = new Mock<IFoo>();     mock.Setup((m) => m.ReturnSomething()).Returns(1);      mock.Verify((m) => m.ReturnSomething(), Times.AtLeastOnce()); } 

If I wanted to verify the number of calls (say twice), is this the only way, where the expression is repeated for the Setup and Verify?

void Test() {     var mock = new Mock<IFoo>();     mock.Setup((m) => m.ReturnSomething()).Returns(1);      mock.Verify((m) => m.ReturnSomething(), Times.Exactly(2)); } 

I just don't like having to call Setup and Verify. Well, since this is a good idea for AAA, to rephrase, I don't like having to repeat the expression for the Setup and Verify. At the moment I store the expression in a variable and pass it to each method, but doesn't feel so clean.

PS - The context for this is for a test checking when a cache is updated or not (expirations etc.)

like image 556
GregS Avatar asked Mar 12 '13 12:03

GregS


People also ask

What is times once in MOQ?

Between - Specifies that a mocked method should be invoked between from and to times. Exactly - Specifies that a mocked method should be invoked exactly times times. Never - Specifies that a mocked method should not be invoked. Once - Specifies that a mocked method should be invoked exactly one time.

How MOQ works?

Moq, how it works The idea is to create a concrete implementation of an interface and control how certain methods on that interface responds when called. This will allow us to essentially test all of the paths through code.


2 Answers

I have this problem all the time. I use strict mocks, and I want to specify strictly (i.e. I used It.Is<>() instead of It.IsAny()) as well as verify strictly (i.e. specifying Times). You cannot use verifiable for this sadly, because Moq is missing a Verifiable(Times) overload.

The full expression of the call, including It.Is<>() is generally big. So in order to avoid duplication I generally resort to the following:

Expression<Action<MockedType>> expression = mockedTypeInstance => mockedTypeInstance.MockedMethod(It.Is<TFirstArgument>(firstArgument => <some complex statement>)/*, ...*/); _mock.Setup(expression);  /* run the test*/  _mock.Verify(expression, Times.Once); 

Not extremely readable, but I don't think there is another way to both use strict setup and strict verification.

like image 89
Evren Kuzucuoglu Avatar answered Sep 21 '22 17:09

Evren Kuzucuoglu


To answer the first question, yes the two blocks are equivalent. Both will fail when .Verify is called if the method on the mock wasn't called.

You can't specify the verify up front as far as I am aware and if you think about it, it makes sense.

This is specifying the behavior of the mock:

mock.Setup(m => m.ReturnSomething()).Returns(1); 

This is verifying the behavior of the caller:

mock.Verify(m => m.ReturnSomething(), Times.AtLeastOnce()); 

Personally I prefer calling verify individually to confirm the required behavior of the caller, the .Verifiable() and .Verify() are shortcuts that are less strict (they just check the method was called one or more times) however if you know your code should only call a method once, put the verify in at the end to confirm it.

I started doing that after a code merge resulted in a method being called twice, the test still passed since it was called at least once but it also meant that something else happened multiple times which shouldn't have!

like image 29
Trevor Pilley Avatar answered Sep 25 '22 17:09

Trevor Pilley