Introduction
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 Class Library DataAccessLayer
contains a DatabaseTransactions
class, which inherits from DbContext
, and the DataAccessLayer contains an Entity class ProductEntity
and a Repository class ProductRepository
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.
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
{
[Test]
public void AddProduct_OnSuccessful_ShouldReturnOne()
{
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);
int result = repository.AddProduct(_ProductEntity);
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.