An introduction to unit testing with Moq

Moq is a framework that allows you to mock your dependencies quickly and carry on unit testing. Moq uses Lambda to create returning results and expectations. Moq uses codegeneration and mocks interfaces, virtual methods and even protected ones. Moq makes your test run faster. A way faster.

Getting started

You can download Moq directly from Google code. At the moment of writing this post, Moq was at version 4.0.10827

Or you can intstall Moq using Nuget Package Console:

Install-Package Moq

Playing around

I’d like to start with this simple example of TraderService class having it’s IsTradeRequestAlreadyExist requested from TraderRepository


    public class TraderService
    {
        public bool IsTradeRequestAlreadyExists(int requesterId, int approverId)
        {
            if (requesterId <= 0 || approverId <= 0)
            {
                throw new Exception("Requester/Approver are not valid");
            }

            TraderRepository traderRepository = new TraderRepository();
            return traderRepository.IsTradeRequestAlreadyExists(senderId, receiverId);
        }
    }

    public class TraderRepository
    {
        public bool IsTradeRequestAlreadyExists()
        {
            // Implementation hided
        }
    }


I intentionally haven't listed an unideal scenario and soon I will explain why: notice that our TraderService class depends on TraderRepository and creates the TraderRepository instance in its IsTradeRequestAlreadyExists method.

I think it is not a good idea to instantiate TraderRepository (which may take it's data from a database or configuration file) that way because:

  • each time when you want to replace TraderRepository with a different implementation you have to modify TraderService class
  • it is hard to unit test because TraderService class is hard coded to query database.

Unit tests should never depend on a database or a configuration file. You should use a mock repository, but it is not possible with existing design. It can be solved by injecting repository into the method but first, we need to refactor TraderRepository class into an interface:


    public interface ITraderRepository
    {
        bool IsTradeRequestAlreadyExists();
    }

    public class TraderRepository : ITraderRepository
    {
        // Implementation hided
    }


Then provide ITraderRepository as method paramater which is called setter injection:


    public class TraderService
    {
        public bool IsTradeRequestAlreadyExists(int requesterId, int approverId, ITraderRepository traderRepository)
        {
            if (requesterId <= 0 || approverId <= 0)
            {
                throw new Exception("Requester/Approver are not valid");
            }

            return traderRepository.IsTradeRequestAlreadyExists(senderId, receiverId);
        }
    }


As an option you may also provide ITraderRepository as constructor parameter which is called constructor injection:


    public class TraderService
    {
        private ITraderRepository _traderRepository;
        
        public TraderService(ITraderRepository traderRepository)
        {
            _traderRepository = traderRepository;
        }

        public bool IsTradeRequestAlreadyExists(int requesterId, int approverId)
        {
            if (requesterId <= 0 || approverId <= 0)
            {
                throw new Exception("Requester/Approver are not valid");
            }

            return _traderRepository.IsTradeRequestAlreadyExists(senderId, receiverId);
        }
    }


Finally, we can mock our TraderRepository like this:


    [TestMethod]
    public void IsTradeExists_BetweenTraders()
    {
        // Given
        int tr1Id = 1, tr2Id = 2;
        //Create a mock
        Mock mockTraderRepository = new Mock();
        // Let say for example, there is no trade berween trader 1 and 2
        mockTraderRepository.Setup(t => t.IsTradeRequestAlreadyExists(tr1Id, tr2Id)).Returns(false);
        // Create trader service
        TraderService traderService = new TraderService(mockTraderRepository.Object);


And do behaviour and state verification:


        // When retrieve the tax
        bool isExist = trader.IsTradeRequestAlreadyExists(tr1Id, tr2Id);
        
        // Then 
        Assert.IsFalse(isExist);
        // Verify that IsTradeRequestAlreadyExists method was called from  the interface
        mockTraderRepository.Verify(t => t.IsTradeRequestAlreadyExists(tr1Id, tr2Id));
    }


The first assert verifies state. We expect false to be returned for a trader Ids 1 and 2
The second verification checks the behaviour by making sure IsTradeRequestAlreadyExists was called. Once it is done we assume that expected behaviour was confirmed. Pretty easy 🙂

Useful tips

Now I'd like to cover some important aspects which most of beginners don't know when they start.

1. Cover with Moq only tests with dependencies and some logic.
For example, the method below should be skipped and do not covered with unit-tests because here we can see the dependency but no logic:


    public Contract GetContractById(int id)
    {
        return _traderRepository.GetContractById(id);
    }

There is only direct call of repository here. Using Moq is absolutely pointless, moreover, this method should not be covered and skipped from unit-testing at all. Of course, you can go on if you keen to achive 100% coverage for your code but still there is no sense. It's ok to write integration test for this method in order to have testing on real data and run it once in a while, but the unit tests should be as fast as they can.

2. If you work on improving existing tests using Moq, I suggest you to start writing a new test from a scratch. Take a look at the method you you want to test, try to understand what the method does and then start implementing the new unit test using Moq. Do not try to copy everything from an old unit test. You don’t need this. Just put a call of the method you’d like to test and provide fake data it wants. At this stage they could be any, we don’t care, for example:


    // Given
    int _requesterId = 1, _approverId = 2;
    // When 
    int tradeRequestId = _traderService.RequestTrade(_requesterId, _approverId);


Now provide some Setup for the method you’d like to mock. That will determine methods of behaviour.


    int expectedRequestId = 3;
    // Mock trade request
    _mockTraderRepository.Setup(t => t.RequestTrade(requesterId, approverId)).Returns(expectedRequestId);


Run your test and make sure that it pass or watch for any problems it has. It could throw exception due some inner logic or wrong data:

Test method TraderProcessing.Test.TestTraderService.RequestTrade_BetweenExistingTraders threw exception: System.Exception: TradeRequest failed since approver does not allow trade requests at TraderProcessing.ITraderService.TraderService.RequestTrade(Int32 requesterId, int32 approverId) in TradeService.cs: line 243 at TraderProcessing.Test.TestTraderService.RequestTrade_BetweenExistingTraders) in TestTraderService.cs: line 151

Take a look at these errors carefully: click on the first url and try to investigate which method (or methods) is causing problems.

In my case it was IsValidTraderRequest method, which was calling _traderRepository.IsAllowTradeRequest and also _traderRepository.IsTradeRequestAlreadyExists method. Because the state of objects in my test was not the same as in real conditions, test failed. I need to fake these repository methods as well.

That mean I need implement moq for these repository methods. Very easy:


    // Moq IsAllowTradeRequest (allowed)
     _mockTraderRepository.Setup(t => t.IsAllowTradeRequest(approverId).Returns(true);
     // Moq IsTradeRequestAlreadyExists (No existing requests)
     _mockTraderRepository.Setup(t => t.IsTradeRequestAlreadyExists(_requesterId, _approverId)).Returns(false);


it’s time to add some validation! Don’t forget to put Verify repo calls:


     // When
     Assert.AreEqual(_expectedRequestId, tradeRequestId);
     // Verify repo calls
     _mockTradeRepository.Verify(repo => repo.RequestTrade(requesterId, approverId), Times.Exactly(1));


The final result (I use MS Test) looks like:


    [TestMethod]
    public void RequestTrade_BetweenExistingUsers ()
    {
        // Given
        int _requesterId = 1, approverId = 2, expectedRequestId = 3;
        // Moq IsAllowTradeRequest
        _mockTraderRepository.Setup(t => t.IsAllowTradeRequest(approverId).Returns(true);
        // Moq IsTradeRequestAlreadyExists (No existing requests)
        _mockTradeRepository.Setup(t => t.IsTradeRequestAlreadyExists(requesterId, approverId)).Returns(false);
        // Mock RequestTrade
        _mockTradeRepository.Setup(t => t.RequestTrade(requesterId, approverId)).Returns(expectedRequestId);
        // Then
        int tradeRequestId = _traderService.RequestTrade(requesterId, approverId, false);
        // When
        Assert.AreEqual(expectedRequestId, tradeRequestId);
        // Verify repo calls
        _mockTraderRepository.Verify(repo => repo.RequestTrade(requesterId, approverId, false), Times.Exactly(1));
    };


Try to split your test into three sections: create all the dependencies required by method you’re going to test, run your method, verify test results using Assert. I prefer mark pieces of code using Given, Then, When comments.


3. It is good to put some Setup things inside separate methods or even classes. Especially if you are going to use them further in other test cases. That will increase code readability and make it even cleaner:


    public void GivenTwoExistingUsersWithNoTrade()
    {
        // Moq IsAllowTradeRequest
        _mockTraderRepository.Setup(t => t.IsAllowTradeRequest(_approverId).Returns(true);
        // Moq IsTradeRequestAlreadyExists (No existing requests)
        _mockTraderRepository.Setup(t => t.IsTradeRequestAlreadyExists(_requesterId, _approverId)).Returns(false);
        // Mock TradeRequest
        _mockTradeRepository.Setup(t => t.RequestTrade(requesterId, approverId)).Returns(expectedRequestId);
    }
    
    [TestMethod]
    public void RequestTrade_BetweenExistingUsers()
    {
        // Given
        GivenTwoExistingUsersWithNoTrade();
        // Then
        int tradeRequestId = _traderService.RequestTrade(requesterId, approverId);
        // When
        Assert.AreEqual(expectedRequestId, tradeRequestId);
        // Verify repo calls
        _mockTraderRepository.Verify(repo => repo.RequestTrade(requesterId, approverId), Times.Exactly(1));
    };


4. Sometimes it is very important in which order you are calling your Moq Setup methods. Keep in mind.

5. It is also very important in which way to provide arguments into moq methods
Let's say, for example you have determined setup for repository as


    _mockTraderRepository.Setup(t => t.ApproveRequest(invitationId, approverId, defaultSuccessCount)).Returns(expectedTradeId);

Where defaultSuccessCount declared as


    int defaultSuccessCount = 3;


But inside implementation of ApproveRequest in TraderService class you have a call and the last argument, minimumReqSuccessCount could have any different value, which will lead to fail your moq test:


    int tradeId = _ mockTraderRepository.ApproveRequest(invitation.Id, approverId, minimumReqSuccessCount);


So in that case you better determine your mock either with the same value


    _mockTraderRepository.Setup(t => t.ApproveRequest(invitationId, approverId, defaultSuccessCount)).Returns(expectedTradeId);

Or try to use It.IsAny like:


    _mockTraderRepository.Setup(t => t.ApproveRequest(invitationId, approverId, It.IsAny)).Returns(expectedTradeId);

But I highly recommend do not use general form It.IsAny(), is not very nice.


6. Concentrate on one method only. Do not call another method to check for other's method result in unit test. I mean, if for example, you test some method which modifies data like UpdateObjectblabla(), do not use another method like GetChangedObject to verify changed status. Here is not a good example listed, because you are calling two different method related to repository:


    // When
    _traderService.UpdateContractRule(_contractRule.Id, newContractRule.TransportRuleId, _contractId);

    // Then (not a good idea to call Get method)
    var updatedRule = _traderService.GetTradeRuleByID(_contractRule.Id);
    Assert.AreEqual(_contractRule.Id, updatedRule.Id);
    Assert.AreEqual(_contractRule.TransportRuleId, updatedRule.TransportRuleId);


Instead, use mock object which you already defined at your code to check results. That means that you have to imitate repo behaviour applied on your data:


    public void DeleteContractRules_BetweenExistingTraders()
    {
        // Given
        GivenContractRules();
        var contractRules = _traderService.GetContract(_contractId);
        var contractRule = contractRules[0];
        int contractRulesCountBeforeDeletion = contractRules.Count;

        // Here we are immitating that our local _contract was deleted
        _mockTraderRepository.Setup(t => t.DeleteContract(It.IsAny()))
        .Callback((int id) =>
        {
            ContractRule contractRule = _contractRules.Find(x => x.Id == id);
            if (contractRule!= null) _contractsRules.Remove(contractRule);
        });
        
        // When
        _traderService.DeleteContractRule(contractRule.Id);

        // Then
        _mockTraderRepository.Verify(repo => repo.DeleteContractRule(contractRule.Id), Times.Exactly(1));
        int contractRulesCountAfterDeletion = _contractsRules.Where(d => d.Direction == "Receive").ToList().Count;
        Assert.AreEqual(contractRulesCountBeforeDeletion - 1, contractRulesCountAfterDeletion);
    }


7. Feel the difference between mocking service methods and mocking repository. Someday I will explain this in detail.


8. Test for exceptions as well. MsTest provide an ability to test an exception. To do this, add the ExpectedException attribute to your method:


    [TestMethod]
    [ExpectedException(typeof(Exception))]
    //Exception message: System.Exception: Trade Request already exists       
    public void RequestTrade_BetweenSameTrader()
    {
        // Given
        GivenTwoExistingUsersWithNoTrade();
        // Then
        int tradeRequestId = _traderService.RequestTrade(requesterId, requesterId);
    };


9. In addition, few more examples of Setup methods

An example of Callback setup:


    _mockTraderRepository.Setup(t => t.DeleteContract(It.IsAny()))
    .Callback((int id) =>
    {
        ContractRule rule = _contractRules.Find(x => x.Id == id);
        if (rule != null) _contractRules.Remove(rule);
    });


An example of Return setup


    _mockTraderRepository.Setup(t => t.GetTradeRuleByID(It.IsAny())).Returns(
        new TradeRule()
        {
            Id = 1,
            TraderId = 2,
            Name = "",
        });


An example of Return setup which depends on argument


    _mockTraderRepository.Setup(t => t.GetTradeRuleByID(It.IsAny()))
    .Returns((int id) =>
    {
        ContractRule rule = _contractRules.Find(x => x.Id == id);
        return rule;
    });


Example of Callback setup plus return value


    _mockTraderRepository.Setup(
        t => t.AddContract(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0)
        .Callback((int fieldTypeId, string senderValue, string receiverValue) =>
        {
        });


Be carefull with lambda expressions and especially with parentheses!

In case If one of your arguments has out modifier, you should use


    _mockTraderRepository.Setup(
        t => t.TradeRequestAlreadyExists(It.IsAny(), It.IsAny(), It.IsAny(), out existingContractId))
        .Returns(true);


10. When you finish test implementation, you have to break the method you're testing and make sure the test fails. Otherwise your test sucks 🙂


As conclusion
Some time ago I wrote around 120 unit tests with Moq in order to replace integration tests which consumed around 40 minutes to pass. That cost me around two weeks of work but as result, coverage was increased upto 85% and more important, testing time was reduced down to 4 seconds. Not too bad, what do you think?

Stay tuned. Subscribe via Email, RSS or Feedly!

Join 164 other followers and receive monthly newsletter and free bonus

Ivan Grigoryev's Blog
Living in New Zealand. Blogging about the country, beautiful places, everyday life.
Do a skydive - halfway completed; get 1400 - still working on; reach 300kph - completed by 96.6%