DEV Community

Cover image for SparkyTestHelpers: Moq
Brian Schroer
Brian Schroer

Posted on • Edited on

2 3

SparkyTestHelpers: Moq

SparkyTestHelpers.Moq:
NuGet package | Source code | API documentation
SparkyTestHelpers.Moq.Fluent:
NuGet package | Source code | API documentation

I really like Moq, “the most popular and friendly mocking framework for .NET”, but I find some of the syntax to be a bit unwieldy.

The SparkyTestHelpers.Moq and SparkyTestHelpers.Moq.Fluent NuGet packages provide extension methods that allow you to use Moq with “wieldier” (Is that a word?) syntax.

SparkyTestHelpers.Moq.Fluent uses Fluent Assertions to provide a more natural language-like verification syntax. If you don't want that dependency, you can use SparkyTestHelpers.Moq. I'll discuss the verification syntaxes below, but both packages provide alternate "Any" syntax and the .Where and .Expression extension methods...

“Any” - Syntax alternative to “It.IsAny”

mock.Setup(x => x.DoSomething(
    It.IsAny<string>(), It.IsAny<int>(), It.IsAny<IEnumerable<int>>())
    .Returns(true);
Enter fullscreen mode Exit fullscreen mode

…can be simplified to:

using SparkyTestHelpers.Moq;
. . .
mock.Setup(x => x.DoSomething(
    Any.String, Any.Int, Any.IEnumerable<int>()) 
    .Returns(true);
Enter fullscreen mode Exit fullscreen mode

“Any” members:

  • Any.Boolean
  • Any.DateTime
  • Any.Decimal
  • Any.Double
  • Any.Int
  • Any.Object
  • Any.String
  • Any.InstanceOf<T>()
  • Any.Array<T>()
  • Any.IEnumerable<T>()
  • Any.List<T>()
  • Any.Dictionary<TKey, TValue>
  • Any.KeyValuePair<TKey, TValue>
  • Any.Tuple<T1, T2>
  • (several more members for all of the intrinsic .NET types)

mock.Where extension method

…provides an alternate syntax for “It.Is”:

mock.Setup(x => x.Foo(It.Is<int>(i => i % 2 == 0))).Returns(true);
Enter fullscreen mode Exit fullscreen mode

...can be written as:

mock.Setup(x => x.Foo(Any.Int.Where(i => i % 2 == 0))).Returns(true);
Enter fullscreen mode Exit fullscreen mode

mock.Expression extension method

…makes it easy to create a reusable expression so you don’t duplicate code in “.Setup” and “.Verify” calls. This test:

// Arrange:
_mock.Setup(x => 
    x.Foo(Any.String, Any.Int, Any.InstanceOf<Bar>()))
    .Returns(true);

// Act:
subjectUnderTest.Fooify("yo", 5, myBar);

//Assert:
_mock.VerifyOneCallTo(x => 
    x.Foo(Any.String, Any.Int, Any.InstanceOf<Bar>()));
Enter fullscreen mode Exit fullscreen mode

…where you have to code the same “x => x.Foo(
Any.String, Any.Int, Any.InstanceOf()
” expression for both the .Setup and .Verify calls — can be simplified to:

using SparkyTestHelpers.Moq;
. . .
// Arrange:
var fooExp = _mock.Expression(x => 
    x.Foo(Any.String, Any.Int, Any.InstanceOf<Bar>()));

_mock.Setup(fooExp).Returns(true);

// Act:
subjectUnderTest.Fooify("yo", 5, myBar);

// Assert:
_mock.VerifyOneCallTo(fooExp);
Enter fullscreen mode Exit fullscreen mode

…so you only have to code the expression once, reducing finger fatigue and the possibility of the Setup and Verify expressions not matching because of a typo!


Alternate Verify syntax with SparkyTestHelpers.Moq.Fluent

The “out of the box” Moq syntax:

mock.Verify(x => x.Foo());
mock.Verify(x => x.Foo(), Times.Once);
mock.Verify(x => x.Foo(), Times.AtLeastOnce);
mock.Verify(x => x.Foo(), Times.AtMostOnce);
mock.Verify(x => x.Foo(), Times.Exactly(3));
mock.Verify(x => x.Foo(), Times.Between(2, 5));
mock.Verify(x => x.Foo(), Times.AtLeast(2));
mock.Verify(x => x.Foo(), Times.AtMost(5));
mock.Verify(x => x.Foo(), Times.Never);
Enter fullscreen mode Exit fullscreen mode

…can be coded fluently as:

mock.Should().HaveCallsTo(x => x.Foo());
mock.Should().HaveOneCallTo(x => x.Foo());
mock.Should().HaveAtLeastOneCallTo(x => x.Foo());
mock.Should().HaveAtMostOneCallTo(x => x.Foo());
mock.Should().HaveCallCount(3).To(x => x.Foo());
mock.Should().HaveCallCount(2, 5).To(x => x.Foo());
mock.Should().HaveCallCount(2).OrMore().To(x => x.Foo());
mock.Should().HaveCallCount(5).OrLess().To(x => x.Foo());
mock.Should().NoCallsTo(x => x.Foo());
Enter fullscreen mode Exit fullscreen mode

…or:

mock.Method(x => x.Foo()).Should().HaveBeenCalled();
mock.Method(x => x.Foo()).Should().HaveBeenCalledOnce();
mock.Method(x => x.Foo()).Should().HaveBeenCalledAtLeastOnce();
mock.Method(x => x.Foo()).Should().HaveBeenCalledAtMostOnce();
mock.Method(x => x.Foo()).Should().HaveCallCount(3);
mock.Method(x => x.Foo()).Should().HaveCallCountBetween(2, 5);
mock.Method(x => x.Foo()).Should().HaveCallCountOfAtLeast(2);
mock.Method(x => x.Foo()).Should().HaveCallCountOfAtMost(5);
mock.Method(x => x.Foo()).Should().NotHaveBeenCalled();
Enter fullscreen mode Exit fullscreen mode

There are property Get and Set equivalents for all of the “.Method … .Should… .HaveBeen” methods listed above, e.g.:

mock.Get(x => x.Bar).Should().HaveBeenCalledOnce();
mock.Set(x => x.Bar = "Baz").Should().HaveBeenCalledOnce();
Enter fullscreen mode Exit fullscreen mode

Alternate Verify syntax with SparkyTestHelpers.Moq

_mock.Verify(x => x.Foo("bar", 3), Times.Once);
Enter fullscreen mode Exit fullscreen mode

…can be coded as:

using SparkyTestHelpers.Moq;
. . .
_mock.VerifyOneCallTo(x => x.Foo("bar", 3));
Enter fullscreen mode Exit fullscreen mode

mock.Verify… extension methods:

  • mock.VerifyCallCount(int count, expresssion)
  • mock.VerifyOneCallTo(expression)
  • mock.VerifyAtLeastOneCallTo(expression)
  • mock.VerifyAtMostOneCallTo(expression)
  • mock.VerifyNoCallsTo(expression)
  • mock.VerifyGetCount(int count, expresssion)
  • mock.VerifyOneGet(expression)
  • mock.VerifyAtLeastOneGet(expression)
  • mock.VerifyAtMostOneGet(expression)
  • mock.VerifyNoGets(expression)
  • mock.VerifySetCount(int count, expresssion)
  • mock.VerifyOneSet(expression)
  • mock.VerifyAtLeastOneSet(expression)
  • mock.VerifyAtMostOneSet(expression)
  • mock.VerifyNoSets(expression)

Happy Testing!

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️