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
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
{
/// <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.