Test driven development (TDD) through Mocking using Moq framework

How TDD is done? How Moq framework helps TDD?

I am going to demonstrate the use of a very popular mocking framework, Moq, to mock database calls.

The example in the article is an Order Processing class, mimicking the order processing system. For the sake of simplicity, I am focusing on fetching an order from database (by order Id), adding 10% GST on the amount and then saving it back to database.

public class OrderProcessing
{
    // object responsible for database operation
    DBContext dbContext = new DBContext();

    public Order ProcessGSTForNextOrder(int orderId)
    {
        var nextOrder = dbContext.GetNextOrderDetailFromDB(orderId);

        nextOrder.Amount = CalculateTotalAmountWithGST(nextOrder);

        dbContext.SaveOrder(nextOrder);

        return nextOrder;
    }

    public decimal CalculateTotalAmountWithGST(Order order)
    {
        return order.Amount + (order.Amount * 10 / 100);
    }
}

Assume an unit test for that. I will passing an order id, in this case 1234, into the processor function, get the output and compare the final amount.

[TestMethod]
public void TestOrderProcessing()
{
     int dummyOrderId = 1234;
     OrderProcessing orderProcessing = new OrderProcessing();
     var modifiedOrder = orderProcessing.ProcessGSTForNextOrder(dummyOrderId);

     Assert.IsTrue(modifiedOrder.Amount == 1100);
 }

This unit test will work if this order id is present in the database. For the sake of argument let's assume it is present in development database. But we cannot guarantee about UAT or production. Also by rule we are not allowed to change any UAT or production data for unit testing. At the end of the day the reason of writing unit case is to test the business logic. Database operation should not interfere at all while unit testing.

To overcome this situation we will be using mocking of database method.

In order to mock database operation, I created an interface IDBcontext. Instead of creating object of DBContext at class level I will be injecting IDBContext in “ProcessGSTForNextOrder” method along with order id. You might be wondering why we created interface, the answer will be if we need to mock any method it should be part of the interface. In this instance we will be mocking two methods “GetNextOrderDetailFromDB” and “SaveOrder”. Rest of the functionality remains same.

public interface IDBContext
{
    Order GetNextOrderDetailFromDB(int orderID);

    void SaveOrder(Order nextOrder);
}

public class OrderProcessingWithMoq
{
    public Order ProcessGSTForNextOrder(IDBContext dbContext, int orderId)
    {
        var nextOrder = dbContext.GetNextOrderDetailFromDB(orderId);

        nextOrder.Amount = CalculateTotalAmountWithGST(nextOrder);

        dbContext.SaveOrder(nextOrder);

        return nextOrder;
    }

    public decimal CalculateTotalAmountWithGST(Order order)
    {
        return order.Amount + (order.Amount * 10 / 100);
    }
}

I have modified unit test case.

[TestMethod]
public void TestOrderProcessing()
{
        int orderId = 1234;

        Mock<IDBContext> mockDBContext = new Mock<IDBContext>();

        mockDBContext.Setup(t => t.GetNextOrderDetailFromDB(It.IsAny<int>())).Returns(new Order() { OrderId = orderId, Amount = 1000 });
        mockDBContext.Setup(t => t.SaveOrder(It.IsAny<Order>()));

        OrderProcessingWithMoq orderProcessing = new OrderProcessingWithMoq();
        var modifiedOrder = orderProcessing.ProcessGSTForNextOrder(mockDBContext.Object, orderId);

        Assert.IsTrue(modifiedOrder.Amount == 1100);
 }

Let's look how it has been setup.

I have created mocking object of DBContext by this statement. This statement says we will be potentially mocking few methods or properties of the interface.

Mock<IDBContext> mockDBContext = new Mock<IDBContext>();

Once the mocking object is ready I will be mocking two methods.

First “GetNextOrderDetailFromDB”...

mockDBContext.Setup(t => t.GetNextOrderDetailFromDB(It.IsAny<int>())).Returns(new Order() { OrderId = orderId, Amount = 1000 });

This will setup mocking object for "GetNextOrderDetailFromDB" method for “any” (It.IsAny()) integer return an Order object with amount 1000 and order id setup.

Next "SaveOrder” has been mocked for “any” Order passed as a parameter...

mockDBContext.Setup(t => t.SaveOrder(It.IsAny<Order>()));

The “orderProcessing” object will have the mocking IDBContext object injected.

var modifiedOrder = orderProcessing.ProcessGSTForNextOrder(mockDBContext.Object, orderId);

The flow will go on from here, executing and returning dummy order and I can ensure the business logic for calculating GST is working correct.

Assert.IsTrue(modifiedOrder.Amount == 1100);
Chinmay C Dey Technology Enthusiast, .net Full Stack Developer, Blogger, Foodie and Self-Styled Conservationist
  • TDD
  • Mock
  • Moq
By Chinmay C Dey On 28 Nov, 18  Viewed: 81