New England Code Camp 17: Make Moq Rock

by Larry Spencer Friday, March 30, 2012 7:40 AM

At New England Code Camp 17 tomorrow, I will give a talk called Make Moq Rock. This post contains the code examples. It is in lieu of the usual PowerPoint slide show and not meant to be a complete tutorial on Moq. The Moq Quick Start already does an excellent job of that.

The examples I will present are all unit tests for a little project I did to compare strategies in the game of Crazy Eights. If you were not at the talk, and want to get the overall context, you can download the complete Visual Studio solution here: CrazyEights.zip (215.37 kb).

By the way, the "smart" strategy (see the code) wins 55% of the time when played against someone who just follows suit or rank. I also invite you to write a strategy of your own. Email it to me and I'll post it!

Now for the code examples.

 

 

A Very Basic Test

 

[TestMethod]
public void Play_IfEight_ThenRemovesCardFromPlayersHand()
{
    // Arrange
    var game = new CrazyEightsGameModel(_stackedDeck);
    var player1 = new Mock<ICrazyEightsPlayer>();
    var player2 = new Mock<ICrazyEightsPlayer>();
    game.AddPlayer(player1.Object);
    game.AddPlayer(player2.Object);
    game.Start();
    var cardToPlay = new Card(Rank.Eight, Suit.Diamonds);

    // Act
    game.Play(CrazyEightsPlay.PlayEight(cardToPlay, Suit.Spades));
    
    // Assert
    player1.Verify(p => p.RemoveCard(cardToPlay), Times.Once());
}

 

  • The players are created as simple, default mocks.
  • Use the Object property to access the mocked object.
  • The Verify method will throw an Exception if p.RemoveCard was not called with the parameter value given.
  • Times.Once() ensures that the method was called exactly once.
  • The Arrange / Act / Assert pattern is favored by many practitioners of unit testing.

 

 

It.IsAny<T> and Times.Exactly

 

[TestMethod]
public void Play_IfDraw_ThenAddsOneCardToPlayersHand()
{
    // Arrange
    _game.AddPlayer(_looseMockPlayer1.Object);
    _game.AddPlayer(_looseMockPlayer2.Object);
    _game.Start();

    // Act
    _game.Play(CrazyEightsPlay.Draw());

    // Assert
    _looseMockPlayer1.Verify(p => p.AddCard(It.IsAny<Card>()), Times.Exactly(8+1));
}

 

  • It.IsAny<Card> allows the Verify to match a call with any Card.
  • Times.Exactly(8+1) ensures that AddCard was called 9 times for the player: 8 to deal his hand and then 1 more when he drew a card. Also useful are Times.Never() and Times.AtLeastOnce().

 

 

It.Is<T> and the Verify Family

 

[TestMethod]
public void Play_IfNormal_ThenRemovesCardFromPlayersHand()
{
    _game.AddPlayer(_looseMockPlayer1.Object);
    _game.AddPlayer(_looseMockPlayer2.Object);
    _game.Start();

    var cardToPlay = new Card(Rank.Ace,   Suit.Diamonds);
    _looseMockPlayer1
        .Setup(p => p.RemoveCard(It.Is<Card>(c => c.Equals(cardToPlay))))
        .Verifiable();
    _game.Play(CrazyEightsPlay.Play(cardToPlay));

    _looseMockPlayer1.Verify();  // VerifyAll() would work even if Verifiable() had not been 
}

 

  • It.Is<T> takes a Lambda. The setup is matched if the Lambda returns true. The example shown is equivalent to simply .Setup(p => p.RemoveCard(cardToPlay))
  • As an alternative to verifying your expectations in the 'Assert' phase, you can mark a setup with Verifiable().
  • Then, you can just call Verify() at the end. It will cause the test to fail if the verifiable setups on that mock were not executed. The usual Times methods are available.
  • Yet another alternative is not to bother with Verifiable() but then call VerifyAll() at the end. VerifyAll will verify every setup whether it was marked Verifiable or not.

 

 

Setting Up Properties

 

[TestMethod]
public void Play_IfNormal_ThenAdvancesToNextPlayer()
{
    // An example of setting up a read-write property with SetupProperty
    _looseMockPlayer1.SetupProperty(p => p.Name);
    _looseMockPlayer2.SetupProperty(p => p.Name);
    // Now you can set the property, and get it later in this test.
    _looseMockPlayer1.Object.Name = "Larry";
    _looseMockPlayer2.Object.Name = "Tom";

    _game.AddPlayer(_looseMockPlayer1.Object);
    _game.AddPlayer(_looseMockPlayer2.Object);
    _game.Start();
    // This test failed without these two lines. Why?
    // This is an example of setting up a read-only property.
    _looseMockPlayer1.SetupGet(p => p.CardCount).Returns(8);
    _looseMockPlayer2.SetupGet(p => p.CardCount).Returns(8);

    _game.Play(CrazyEightsPlay.Play(new Card(Rank.Ace, Suit.Diamonds)));

    Assert.AreEqual("Tom", _game.CurrentPlayer.Name);

    // Another call to Play advances back to player 1
    _game.Play(CrazyEightsPlay.Play(new Card(Rank.Ace, Suit.Clubs)));

    Assert.AreEqual("Larry", _game.CurrentPlayer.Name);
}

 

  • Use SetupProperty to make the mock of a property behave like an ordinary getter and setter.
  • Use SetupGet to set up a read-only property.
  • Without the SetupGets that Return 8, the CardCount property for each player would have returned 0 (the default value) because the mocks are 'loose'. The subject-under-test would think neither player had any cards and unexpected things would happen. This sort of strange result is why some people prefer strict mocks.

 

 

Strict Mocks

 

[TestInitialize]
public void TestInit()
{
    _game = new CrazyEightsGameModel(_stackedDeck);
    _looseMockPlayer1 = new Mock<ICrazyEightsPlayer>(MockBehavior.Loose);
    _looseMockPlayer2 = new Mock<ICrazyEightsPlayer>(MockBehavior.Loose);
    _strictMockPlayer1 = new Mock<ICrazyEightsPlayer>(MockBehavior.Strict);
    _strictMockPlayer2 = new Mock<ICrazyEightsPlayer>(MockBehavior.Strict);
}

 

 

[TestMethod]
public void Play_IfEight_ThenAdvancesToNextPlayer()
{
    _strictMockPlayer1.SetupGet(p => p.Name).Returns("Larry");
    _strictMockPlayer2.SetupGet(p => p.Name).Returns("Tom");
    _strictMockPlayer1.SetupGet(p => p.CardCount).Returns(0);
    _strictMockPlayer2.SetupGet(p => p.CardCount).Returns(0);
    _strictMockPlayer1.Setup(p => p.AddCard(It.IsAny<Card>()));
    _strictMockPlayer2.Setup(p => p.AddCard(It.IsAny<Card>()));
    _game.AddPlayer(_strictMockPlayer1.Object);
    _game.AddPlayer(_strictMockPlayer2.Object);
    _game.Start();
    _strictMockPlayer1.SetupGet(p => p.CardCount).Returns(8);
    _strictMockPlayer2.SetupGet(p => p.CardCount).Returns(8);
    _strictMockPlayer1.Setup(p => p.RemoveCard(It.IsAny<Card>()));
    _strictMockPlayer2.Setup(p => p.RemoveCard(It.IsAny<Card>()));

    _game.Play(CrazyEightsPlay.PlayEight(new Card(Rank.Eight, Suit.Diamonds), Suit.Diamonds));
    Assert.AreEqual("Tom", _game.CurrentPlayer.Name);

    // Now advance back to player 1
    _game.Play(CrazyEightsPlay.PlayEight(new Card(Rank.Eight, Suit.Clubs), Suit.Clubs));
    Assert.AreEqual("Larry", _game.CurrentPlayer.Name);
}

 

 

  • This test is similar to the last one, but we use MockBehavior.Strict. Note how the strict and loose mocks were created -- in the [TestInitialize] method. MockBehavior.Loose is the default, so the loose mocks could have been created without the parameter: Mock<ICrazyEightsPlayer>().
  • A loose mock allows you to call any method or property. Those that return values will return null or 0.
  • With a strict mock, you must set up everything that happens. If you don't, you'll get an exception. The advantage is that there are no surprises.

 

Callbacks

 

[TestMethod]
public void AddPlayer_DealsTop8CardsFromStockToPlayer()
{
    var hand = new List<Card>();
    _strictMockPlayer1.Setup(p => p.CardCount).Returns(hand.Count);

    _strictMockPlayer1.Setup(p => p.AddCard(It.IsAny<Card>()))
        .Callback((Card c) => hand.Add(c));

    _game.AddPlayer(_strictMockPlayer1.Object);
    Assert.IsTrue(_stackedDeck.Take(8)
        .OrderBy(c => c.Rank)
        .SequenceEqual(hand.OrderBy(c => c.Rank)));
}

 

  • The Lambda specified in the Callback method is executed each time the method in the preceding Setup is called.
  • The parameters for the Lambda must match the parameters to the method that was set up, or you'll get a run-time error: System.Reflection.TargetParameterCountException: Parameter count mismatch or System.ArgumentException: Object of type 'foo' cannot be converted to type 'bar'. .
  • The Callback has access to variables outside of the mock.

 

 

Supplying a Whole Method

 

[TestMethod]
public void Play_TieAfter1000Turns()
{
    var alwaysPlayEight = new Mock<ICrazyEightsStrategy>();

    alwaysPlayEight.Setup(strategy => strategy.ChoosePlay(It.IsAny<ICrazyEightsGameModel>(), It.IsAny<IEnumerable<Card>>()))
        .Returns((ICrazyEightsGameModel game, IEnumerable<Card> hand) => 
            {
                var eight = hand.FirstOrDefault(card => card.Rank==Rank.Eight);
                if (eight != null)
                    return CrazyEightsPlay.PlayEight(eight, Suit.Spades);
                else
                    return CrazyEightsPlay.Draw();
            });

    _game.AddPlayer(new CrazyEightsPlayer("Player 1", alwaysPlayEight.Object));
    _game.AddPlayer(new CrazyEightsPlayer("Player 2", alwaysPlayEight.Object));
    _game.Start();

    while (true)
    {
        var play = _game.CurrentPlayer.ChoosePlay(_game);
        _game.Play(play);
        if (_game.Turns.Count() <= 1000)
        {
            Assert.IsFalse(_game.IsTie);
            Assert.IsNull(_game.Winner);
        }
        else
        {   
            Assert.IsTrue(_game.IsTie);
            Assert.IsNull(_game.Winner);
            break;
        }
    }
}

 

  • Here we wanted to force the game to go to 1000 turns, which we will call a tie. We created an ICrazyEightsStrategy that always plays an eight, and injected the strategy into each player.
  • The Returns call attached to the Setup of ChoosePlay can take a Lambda as a parameter. The Lambda's parameters are those that were passed to ChoosePlay.
  • If the parameters to Returns' Lambda and ChoosePlay don't match, you'll a run-time error.

 

 

A Mock Layer on a Real Object

 

[TestMethod]
public void AddPlayer_ProhibitedIfGameEnded()
{
    var mockGame = new Mock<CrazyEightsGameModel>();
    mockGame.Object.AddPlayer(_looseMockPlayer1.Object);

    mockGame.SetupGet(g => g.IsFinished).Returns(true);

    Expect.Exception<Exception>(
        () => mockGame.Object.AddPlayer(new Mock<ICrazyEightsPlayer>().Object),
        Properties.Resources.CannotJoinBecauseGameFinished);
}

 

  • Here, the type parameter to new Mock<> is a class, not an interface.
  • On the first reference to mockGame.Object, the class' default constructor is called.
  • From there, the real object's methods and properties will be called, except where you've set up otherwise, as with our SetupGet.
  • A major restriction is that any properties or methods you want to set up must be virtual! (Use Typemock Isolator if that's a deal-breaker.)
  • BTW, the Expect.Exception call is my own little invention. Here's the source for it.

 

static public class Expect
{
    /// <summary>
    /// Assert that the action produces the exception, bearing the message.
    /// </summary>
    /// <typeparam name="TException">The type of Exception expected.</typeparam>
    /// <param name="action">The action to perform.</param>
    /// <param name="expectedMessage">The message that is expected in the Exception.</param>
    /// <param name="exact">If true, the message must match exactly. 
    /// If false, we only expect the Exception's message to contain the given text</param>
    static public void Exception<TException>(Action action, string expectedMessage, bool exact = false)
        where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            if (exact)
                Assert.AreEqual(expectedMessage, ex.Message);
            else
            {
                Assert.IsTrue(
                    ex.Message.Contains(expectedMessage), 
                    String.Format("Expected the exception's Message to contain '{0}'. Instead, the message was '{1}'.", expectedMessage, ex.Message));
            }
            return;
        }
        Assert.Fail("Should have thrown a(n) {0}.", typeof(TException));
    }
}

 

 

Mocking Multiple Interfaces; Sequential Returns; Mocking with Legacy Code

 

We now turn away from Crazy Eights to a normal business application. :(

This code is excerpted from a production test in a SQL Server environment. I've changed it slightly to remove proprietary information.

 

[TestMethod]
public void MyTest()
{
    int myKey = 123;
    int myValue = 789;

    //-------------------------------------------------------------------
    // Mock an IDataReader.
    //-------------------------------------------------------------------
    var mockReader = new Mock<IDataReader>(MockBehavior.Strict);
    var mockReaderDisposable = mockReader.As<IDisposable>();
    mockReaderDisposable.Setup(d => d.Dispose());

    mockReader.SetupSequence(rdr => rdr.Read())
        .Returns(true)
        .Returns(false);
    mockReader.Setup(rdr => rdr.GetValue(0)).Returns(myKey);
    mockReader.Setup(rdr => rdr.GetValue(1)).Returns(myValue);

    //-------------------------------------------------------------------
    // Mock an IDbCommand.
    //-------------------------------------------------------------------
    var mockCommand = new Mock<IDbCommand>(MockBehavior.Strict);
    var mockCommandDisposable = mockCommand.As<IDisposable>();
    mockCommandDisposable.Setup(d => d.Dispose());
    mockCommand.SetupSet(cmd => cmd.CommandType = CommandType.Text);
    mockCommand.SetupSet(cmd => cmd.CommandText = String.Format("SELECT ...proprietary things...
    mockCommand.Setup(cmd => cmd.ExecuteReader()).Returns(mockReader.Object);

    //-------------------------------------------------------------------
    // Mock an IDbConnection
    //-------------------------------------------------------------------
    var mockConnection = new Mock<IDbConnection>(MockBehavior.Strict);
    mockConnection.Setup(cn => cn.CreateCommand()).Returns(mockCommand.Object);

    //-------------------------------------------------------------------
    // Finally execute the method we are testing.
    //-------------------------------------------------------------------
    var result = DocProcs.GetProprietaryThings(mockConnection.Object, 99, new int[] { myKey });

    //-------------------------------------------------------------------
    // Verify that we got the single result we expect.
    //-------------------------------------------------------------------
    Assert.AreEqual(1, result.Count);
    Assert.AreEqual(myValue, result[myKey]);

    //-------------------------------------------------------------------
    // Verify that the expected methods were called and 
    // everything was disposed properly.
    //-------------------------------------------------------------------
    mockReaderDisposable.VerifyAll();
    mockReader.VerifyAll();
    mockCommandDisposable.VerifyAll();
    mockCommand.VerifyAll();
}

 

  • The method we are testing is in the middle (DocProcs.GetProprietaryThings). It takes an IDbConnection and some parameters. We want to verify that it creates and disposes an IDbCommand that has a particular SQL statement. We also want to verify that it properly returns the values from that SQL statement. (This is way more than you'd usually put in one unit test, and the test knows much more than it should about the inner workings of the subject-under-test, but this test was written against legacy code that did not have such niceties as an injected IRepository!)
  • In the first section, we create a Mock<IDataReader>, and then add another mock to it As<IDisposable>. We now have two mocks on the same object.
  • We then set up the mockReaderDisposable to expect a call to Dispose().We will verify that Dispose() was called by using VerifyAll() at the end.
  • Immediately afterward, you'll see a call to SetupSequence. This allows you to return different values on successive calls to a method. In this case, we return true the first time because we want to pretend we got a record, and then false to signify that we have no more records.

 

 

Where to Learn More

 

I hope this post (or my talk, if you attended) has whetted your appetite for Moq. If you want to learn more, here are some resources.

Tags: ,

All | Talks

Pingbacks and trackbacks (1)+

Add comment

About the Author

Larry Spencer

Larry Spencer develops software with the Microsoft .NET Framework for ScerIS, a document-management company in Sudbury, MA.