I am reading, experimenting and hopefully learning how to use Moq is a mocking framework for C#/.NET. To learn more about mocking objects read here. For additional documentation on moq4 read here.
After completing the PluralSight course “Mocking with Moq 4 and xUnit” I decided to look for examples of Moq mocking a database. This post is based on my experience and comments on the article Unit Test Through Mocking Using MOQ Framework by Chinmay Dey published Dec 26, 2018.
If interested in the topic I suggest for you to read the article of interest, attempt to replicate the code, and verify that you can run the unit test that reads an order from the database, updates the amount by the associated tax, updates the record in the database, and returns the updated record. Of course these operations will be mocked using Moq 4.
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting.Contexts; using System.Text; using System.Threading.Tasks; namespace MockDB { /// <summary> /// The example in the article is an Order Processing class, /// mimicking the order processing system. /// The business logic in this class is to fetch an order from a database (by order Id), /// adding 10% GST (Goods and Services Tax) on the amount, /// and then saving it back to the database. /// </summary> public class OrderProcessing { // **** object responsible for database operation **** private IDBContext _dbContext; /// <summary> /// Reads the order from the database. /// Calculates and updates the Amount by the GST (10%). /// Saves the updated record in the database. /// </summary> /// <param name="orderId"></param> /// <returns></returns> public Order ProcessGSTForNextOrder(IDBContext dbContext, int orderId) { // **** **** _dbContext = dbContext; // **** get order by orderId **** var nextOrder = dbContext.GetNextOrderDetailFromDB(orderId); // **** calculate and update the order amount **** nextOrder.Amount = CalculateTotalAmountWithGST(nextOrder); // **** save the updated order in the database **** _dbContext.SaveOrder(nextOrder); // **** return the updated order **** return nextOrder; } /// <summary> /// This method calclates the updated amount for the specified order. /// The amount is incremented by 10%. /// </summary> /// <param name="order"></param> /// <returns></returns> public decimal CalculateTotalAmountWithGST(Order order) { return order.Amount + ((order.Amount * 10) / 100); } } }
In the OrderProcessing class we have two methods: Process GSTForNextOrder() and CalculateTotalAmountWithGST().
The first method gets from the database an order which is specified by the orderId parameter.
The second method calculates the GST (Good and Services Tax) in Australia. The reason is that the author of the article appears to live there. The location is not important. What matters is that we need to update the amount in the order by the value of the GST which happens to be 10%.
It seems this is done for simplicity. In a well designed application the taxes (yes there can be different sets per location) should always be kept in different fields and added to the total amount without changing the amount before taxes.
Also note that the code on the post for the CalculateTotalMountWIthGST() does no work. It returns the Amount. If interested take a look at such code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MockDB { public class Order { public int OrderId { get; set; } public decimal Amount { get; set; } } }
This code defines the Order class. For simplicity it only has two fields: OrderId and Amount. Each field has an associated getter and setter.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MockDB { /// <summary> /// Methods to be mocked. /// </summary> public interface IDBContext { // **** method to be mocked **** Order GetNextOrderDetailFromDB(int orderID); // **** method to be mocked **** void SaveOrder(Order nextOrder); } }
The IDBContext interface defines the two methods that will be mocked. The first reads the order from the database and the second saves the updated order to the database.
using Moq; namespace MockDB.Tests { public class DBUnitTest { /// <summary> /// Save order. /// Read the order. /// Modify the order. /// Verify the proper GST was added to the Amount. /// </summary> [Fact] public void TestOrderProcessing() { // **** dummy order Id **** var orderId = 1234; // **** we will be potentially mocking a few methods or properties of the interface or System Under Test (SUT) **** Mock<IDBContext> mockDBContext = new Mock<IDBContext>(); // **** “SaveOrder” has been mocked for “any” order passed **** mockDBContext.Setup(t => t.GetNextOrderDetailFromDB(It.IsAny<int>())).Returns(new Order() { OrderId = orderId, Amount = 1000 }); mockDBContext.Setup(t => t.SaveOrder(It.IsAny<Order>())); // **** “orderProcessing” object will have the mocking IDBContext object injected **** OrderProcessing orderProcessing = new OrderProcessing(); // **** update the order (add the GST which is 10% of the Amount) **** var modifiedOrder = orderProcessing.ProcessGSTForNextOrder(mockDBContext.Object, orderId); // **** verify the updated order amount (10% of initial Amount) **** Assert.Equal(1100, modifiedOrder.Amount); } } }
The TestOrderingProcesses() unit test specifies a dummy order number.
We then set up the mock for the method that reads the order from the database and the one that saves the updated order to the database.
A new order is created.
The order is read and the Amount is updated.
The Amount is then checked to verify that it has been updated to Amount + 10% of the original Amount.
After building the code we can run the unit test using the Test Explorer in Visual Studio 2022. The result is illustrated as follows:
Hope you enjoyed reading and experimenting with the associated code.
The associated code is in the following two GitHub repositories: code and unit test .
Enjoy;
John