Unit Testing with NUnit: Guidelines for Existing Codebases

In the realm of software development, unit tests are indispensable for code quality and stability. While Test-Driven Development (TDD) is ideal, integrating unit tests into pre-existing codebases can be tricky. This article aims to provide guidelines for writing unit tests using NUnit, for existing codebases.

The Scenario

Consider an Inventory Management System consisting of a Class Library DataAccessLayer and a C# Project InventoryManagementSystem. The DataAccessLayer contains a DatabaseTransactions class, which inherits from DbContext, and the DataAccessLayer contains an Entity class ProductEntity and a Repository class ProductRepository

Project structure as mentioned above

DatabaseTransactions

public class DatabaseTransactions : DbContext
{
   public DatabaseTransactions() : base("name=conStr"){}	
   public virtual DbSet<ProductEntity> Product { get; set; }	
   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
	modelBuilder.Entity<ProductEntity>().ToTable("tblProduct");
    }
}        

ProductEntity

public class ProductEntity
{
   public int ID { get; set; }
   public string ProductName { get; set; }
   public int UnitPrice { get; set; }
   public int Quantity { get; set; }
}        

ProductRepository

public class ProductRepository
{
    private readonly DatabaseTransactions transaction;
	
    public ProductRepository(DatabaseTransactions _transaction) 
    { 
        transaction = _transaction; 
     }
     public int AddProduct(ProductEntity _productEntity)
     {
        try
        {
            transaction.Product.Add(_productEntity);
            return transaction.SaveChanges();      
        }
        catch (Exception ex)
        {
            return -1;
        }
    }
}        

Setting up InventoryManagementSystem.Test Project

Add Unit Test Project InventoryManagementSystem.Test and Incorporate NuGet Packages for NUnit and Moq in the InventoryManagementSystem.Test Project to Facilitate DatabaseTransactionClass Mocking.

Add DataAccessLayer Solution Reference for Mocking DatabaseTransaction and testing ProductRepository methods within InventoryManagementSystem.Test Project

Unit Test Project with dependencies

Writing Unit Tests with NUnit

Identify Testable Units

Before diving into writing tests, it's essential to identify the units of code that can be tested in isolation. In our scenario, the ProductRepository class's AddProduct method is a suitable candidate for testing.

Establishing a Foundation

Add a class ProductRepositoryTest, and mention [TestFixture] which is used to mark a class as containing test fixtures. we adhere to the Arrange, Act and Assert (AAA) principle for unit testing.

[TestFixture]
public class ProductRepositoryTest
{
    /// <summary>
    /// In this class we will add test method, we adhere to the AAA 
    /// (Arrange, Act, Assert) principle:
    /// 
    /// 1 - Arrange:
    /// We initialize the test environment by setting up the necessary 
    /// objects and dependencies. 
    /// This involves creating an instance of the ProductEntity class, 
    /// as well as mocking the ProductEntity and DatabaseTransaction 
    /// classes to simulate their behavior when the AddProduct method 
    ///  is called. we instantiate the ProductRepository class, 
    /// initializing it with the mocked DatabaseTransaction object.
    /// 
    /// 
    /// 2 - Act:
    /// invoke the AddProduct method within the ProductRepository class. 
    /// This step represents the action that we are testing.
    /// 
    /// 
    /// 3 - Assert:
    /// Using assertion syntax, we validate the outcome of the test. 
    /// Specifically, we verify whether the result obtained 
    /// from calling the AddProduct method meets the expected criteria.
    /// </summary>
}        

Method to test successful product addition in ProductRepository class

[TestFixture]
public class ProductRepositoryTest
{
   [Test]
   public void AddProduct_OnSuccessful_ShouldReturnOne()
   {
      //Arrange
      var _ProductEntity = new ProductEntity()
      {
         ProductName = "Computer Table",
         UnitPrice = 25000,
         Quantity = 500,
      };
      var productData = new List<ProductEntity>().AsQueryable(); 
        
      var mockSet = new Mock<DbSet<ProductEntity>>();

      mockSet.As<IQueryable<ProductEntity>>().Setup(m => m.GetEnumerator()).Returns(productData.GetEnumerator());

     mockSet.As<IQueryable<ProductEntity>>().Setup(m => m.Expression).Returns(productData.Expression);

    mockSet.As<IQueryable<ProductEntity>>().Setup(m => m.ElementType).Returns(productData.ElementType);

    mockSet.As<IQueryable<ProductEntity>>().Setup(m => m.Provider).Returns(productData.Provider);
        
    var mockContext = new Mock<DatabaseTransactions>();
    mockContext.Setup(c => c.Product).Returns(mockSet.Object);
    var repository = new ProductRepository(mockContext.Object);
        
    //Act
    int result = repository.AddProduct(_ProductEntity);
       
  //Assert
  Assert.That(result, Is.EqualTo(1));
 }
}        

Conclusion

As showcased in this article, using NUnit and Moq, we can write concise and targeted unit tests for existing code without extensive modifications. By following these guidelines, developers can enhance the maintainability and reliability of their software systems while minimizing disruption to the existing codebase.


要查看或添加评论,请登录

Zohaib Malik的更多文章

社区洞察

其他会员也浏览了