Mock Testing with MOQ

Introduction

In the world of .NET development, unit testing is not just a good practice; it's a necessity. It's the process of testing individual units or components of your code in isolation to ensure they work as intended. But why is it so crucial? Well, for one, it helps catch bugs early in the development process, saving you countless hours of debugging down the road. It also promotes a culture of code quality and reliability, pushing developers to write cleaner, more maintainable code. In today's fast-paced software world, where updates and changes are constant, unit tests provide a safety net, allowing you to confidently refactor and enhance your code without fear of breaking existing functionality. In this article, we'll delve deeper into why unit testing is an indispensable tool for .NET developers and how it can lead to more robust and resilient software solutions. 

What is Mock Testing.?

Imagine you and your team are developing a shopping website. One developer is responsible for creating the "Products" functionality, while you're in charge of the "Add to Shopping Cart" feature. However, the "Products" functionality is not ready yet, and you want to start testing your part of the code. This is where mock testing comes into play. 

Without the "Products" functionality, you might think you have to wait to test your "Add to Shopping Cart" feature. Instead, you can create mock objects that mimic the behavior of the "Products" functionality. These mock objects simulate the presence of actual products without requiring the real "Products" code.

How does mock testing help?


1. Isolation

You can isolate your "Add to Shopping Cart" code by replacing the real "Products" functionality with mock objects. This isolation ensures that any issues you encounter during testing are related to your code and not influenced by the unfinished "Products" functionality.

2. Early Testing

Even if the "Products" functionality isn't complete, you can start testing your part right away. This allows you to catch and fix issues early in the development process, reducing potential bottlenecks.

3. Focused Testing

There may be situations where both the "Products" and "Shopping Cart" functionalities are available, but you want to specifically test your "Add to Shopping Cart" feature. By using mock objects for the "Products" functionality, you can focus your unit tests on your code and its interactions with the shopping cart.

Mock testing allows you to create simulated versions of dependent components or functionalities, enabling you to test your code in isolation. This approach is valuable when certain parts of your software are not yet ready or when you want to focus on testing specific components independently. It enhances your ability to write thorough and reliable unit tests while ensuring that your code functions correctly within the larger system, even in the absence of certain dependencies. We will use MOQ the widely used open source library for creating MOCK Object.

Without further delay, Let's create a console Application in C# using Visual Studio 2022 IDE.

Create a C# Console Application Project

Follow the following steps to create a new project in Visual Studio 2022.

  1. Launch Visual Studio and create a new project by selecting "File" > "New" > "Project."
  2. Select the appropriate project type for your application. For this tutorial, we'll use a Console Application project as an example. Name your project (e.g., "ShoppingCart") and click "Create."

Mock Testing

Now, we will implement the same scenario of the Shopping Cart as discussed above in this article to demonstrate the Mock Testing. Add Product Model Class. 

// Product.cs Class 
public class Product
 {
     public int Id { get; set; }
     public string Name { get; set; }
     public decimal Price { get; set; }
 }

Create IProductRepository Interface, which represents the dependencies of the Shopping Cart system. Define relevant methods such as GetById(int id), which return the product.

// IProductRepository.cs 

public interface IProductRepository
 {
     Product GetById(int id);
 }

Create the ShoppingCart class, which uses these dependencies to perform Shopping Cart operations. In this class, I will Add methods like AddItem(), RemoveItem(), and get ItemCount().

// ShoppingCart.cs

public class ShoppingCart
{
    private readonly IProductRepository productRepository;
    private readonly Dictionary<int, int> items;

    public ShoppingCart(IProductRepository productRepository)
    {
        this.productRepository = productRepository;
        this.items = new Dictionary<int, int>();
    }

    public void AddItem(int productId, int quantity)
    {
        // Fetch product details from the repository
        Product product = productRepository.GetById(productId);

        if (product != null)
        {
            // Add the product to the cart
            if (items.ContainsKey(productId))
            {
                items[productId] += quantity;
            }
            else
            {
                items[productId] = quantity;
            }
        }
    }

    public void RemoveItem(int productId, int quantity)
    {
        if (items.ContainsKey(productId))
        {
            // Remove the specified quantity of the product from the cart
            items[productId] -= quantity;

            // If the quantity reaches zero, remove the item from the cart
            if (items[productId] <= 0)
            {
                items.Remove(productId);
            }
        }
    }

    public int GetItemCount()
    {
        return items.Values.Sum();
    }
}

Now we are done with the writing functionality of the Shopping Cart. Now we need to write test cases to check if those methods work as expected. 

In .NET development, it's best practice to create a dedicated NUnit Test Project for unit tests. This separation enhances code organization and isolates test dependencies from the main project, leading to cleaner and more efficient testing.

Create an NUnit Test Project

Please follow the following steps to Add the NUnit Test Project into our solution.

  • Right-click on your solution in Solution Explorer.
  • Select "Add" > "New Project."
  • Choose "NUnit Test Project (.NET Core)" or an appropriate test project template.
    NUnit Test Project
  • Name your test project (e.g., "ShoppingCartTests") and click "Create."
  • In the test project, add a reference to your main project (in our case, It is a shopping Cart Project).

Install MOQ NuGet Package 

Now, we need to install the MOQ NuGet package in our test Project.  Write the following command in the Package Manager Console.

install-package MOQ

The Package Manager Console will download and install the Moq package and its dependencies into your project.

Once Installation is complete, Add the following class in the NUnitTest Project.

// ShoppingCartTests.cs

 public class ShoopingCartTests
 {

    
     [Test]
     public void AddItem_ValidProduct_AddsToCart()
     {
         // Arrange
         var productRepositoryMock = new Mock<IProductRepository>();
         productRepositoryMock.Setup(repo => repo.GetById(1)).Returns(new Product { Id = 1, Name = "Product A", Price = 10.0m });
         var shoppingCart = new ShoppingCart(productRepositoryMock.Object);

         // Act
         shoppingCart.AddItem(1, 2);

         // Assert
         Assert.AreEqual(2, shoppingCart.GetItemCount());
     }

     [Test]
     public void RemoveItem_ProductExists_RemovesFromCart()
     {
         // Arrange
         var productRepositoryMock = new Mock<IProductRepository>();
         productRepositoryMock.Setup(repo => repo.GetById(1)).Returns(new Product { Id = 1, Name = "Product A", Price = 10.0m });
         var shoppingCart = new ShoppingCart(productRepositoryMock.Object);
         shoppingCart.AddItem(1, 3);

         // Act
         shoppingCart.RemoveItem(1, 2);

         // Assert
         Assert.AreEqual(1, shoppingCart.GetItemCount());
     }

     [Test]
     public void RemoveItem_ProductDoesNotExist_NoChange()
     {
         // Arrange
         var productRepositoryMock = new Mock<IProductRepository>();
         var shoppingCart = new ShoppingCart(productRepositoryMock.Object);

         // Act
         shoppingCart.RemoveItem(1, 2);

         // Assert
         Assert.AreEqual(0, shoppingCart.GetItemCount());
     }
 }

The above code snippet contains a set of unit tests using the Moq mocking framework and NUnit testing framework. These tests assess the behavior of a ShoppingCart class under various scenarios, such as adding and removing items. Moq is employed to create mock instances of IProductRepository, allowing controlled testing without relying on actual database or service calls. The tests are structured into Arrange, Act, and Assert phases, ensuring the proper setup, execution of actions, and verification of expected outcomes for each test case.

 Running the Tests

  1. Build your solution to ensure everything is up to date.
  2. Open the Test Explorer in Visual Studio (Test > Test Explorer).
  3. Click the "Run All" button in the Test Explorer to execute your unit tests.
  4. Review the test results.

Test Explorer

In this way, we have tested our functionality of the shopping cart by creating Mock objects of Products. 

Conclusion

In conclusion, the unit tests showcased in the provided code demonstrate a systematic approach to ensuring the correctness and reliability of the ShoppingCart class in different scenarios. By leveraging the Moq mocking framework and the NUnit testing framework, developers can rigorously test their code, isolate dependencies, and verify expected behaviors. These tests serve as a critical tool in the software development process, enabling early bug detection, maintaining code quality, and enhancing the robustness of .NET applications. With a well-structured suite of unit tests, developers can confidently iterate on their codebase, knowing that changes are validated against a suite of comprehensive tests, ultimately leading to more stable and maintainable software.

I have tried to keep this article simple and easy. Feel free to ask any questions or submit your feedback in the comment section.


Similar Articles