Introduction
Unit testing is very important for creating quality applications. We should make unit testing an important aspect of application development. These unit tests are written by developers. Further, after writing test cases, code coverage must be checked with tools such as SonarQube. A good pattern for writing test cases is Arrange-Act-Assert. It simply means writing test cases in 3 phases: Arrange the configurations, data, or connections that will be involved in test case writing; Act on the unit by invoking the method with required parameters; and Assert on the behavior to validate the scenarios. There is some pre-work required to make our code testable such as using Interfaces instead of actual class when referring from another class but we will not go into more depth in that topic.
Act and Assert is straightforward, they only require developers understanding of that component. Arrange part is the most complex as it requires setting up multiple things to run the test cases. This generally requires creating a dummy method implementation with test data (also known as Mocking) for executing the method and we end up hard-coding values, use random values or sometimes end up putting actual values to execute the test. We do not and should not actually invoke the data access layer for testing our business logic or presentation layers for unit testing. We must actually Mock the methods in the Arrange phase that interact with resources such as file system, database, cache server, etc. Bogus and Moq are very compatible with each other for writing efficient test cases. One will generate the data and the second will mock the method response with generated data.
What is Moq?
Moq is a library for generating Mocks of methods using LINQ. It removes the complexity of writing mock methods, setting up dependencies, and then creating the object. WIthout moq it will require writing a lot of repeated code to just generate the mock classes and methods. A simple example of Moq is,
var dataAccessMock = new Mock < IEmployeeDataAccess > ();
dataAccessMock.Setup(c => c.GetEmployees()).Returns(new List < Employee > {
/*Employee objects*/ });
var employeeDataAccess = dataAccessMock.Object;
It’s nuget package is Moq, In csproj, <PackageReference Include="Moq" Version="4.17.2" />
What is Bogus?
Bogus is a library used to generate fake data in .NET. It uses fluent API to write rules for generating fake data. It has a lot of datasets available that we can reuse to generate test data. It can generate random email ids, locations, uuids, etc. It is a very good library for testing. Some use cases are as shown below like Person, Images, Lorem Ipsum, etc (Screenshot from Package Meta).
Sample code snippet for generating email ids data, it is very simple.
var emailIds = new Faker<string>().RuleFor(c => c, f => f.Person.Email).GenerateBetween(5,6);
It is available as <PackageReference Include="Bogus" Version="34.0.1" />
Setup Requirements
- .NET 6
- Visual Studio 2022
- NUnit
Demo
Open NUnit Project and download Bogus and Moq Packages. For demo purposes, I have created a DataAccessLayer and BusinessLogicLayer project and it contains the EmployeeDataAccess interface and implementation and EmployeeBusinessLogicLayer interface and implementation. We are going to test the business logic layer by mocking the data access layer with Bogus to generate sample data and Moq to perform mocking.
Our Project structure looks like this,
Now, in the Unit Test Project we will follow the code in setup.
private IEmployeeBusinessLogic employeeBusinessLogic;
[SetUp]
public void Setup() {
//Arrange
//Set Id to 0 that will be increased on every iteration by faker object id rule
int id = 0;
//Initialize the mock class and test employees class object
var dataAccessMock = new Mock < IEmployeeDataAccess > ();
var testEmployees = new Faker < Employee > ();
//This contains the rules for generating the fake data.
//It takes 2 parameters, First is property name of employee object and second is replacement value
//The replacement value can be rule based or random data generated from Faker class, eg. f=>f.Person.FullName will generate a fake name
testEmployees.RuleFor(x => x.Id, ++id).RuleFor(x => x.Name, f => f.Person.FullName).RuleFor(x => x.Location, f => f.Address.State()).RuleFor(x => x.EmailId, f => f.Internet.Email()).RuleFor(x => x.EmployeeId, Guid.NewGuid());
//This will set the mock method with response generated from Bogus library
dataAccessMock.Setup(c => c.GetEmployees()).Returns(testEmployees.GenerateBetween(1, 20));
//This will assign the mock data access object to business logic constructor
employeeBusinessLogic = new EmployeeBusinessLogic(dataAccessMock.Object);
}
Explanation
This code has all the logic to replace the data access with fake data that resembles the actual data thanks to Bogus library and Moq package that will implement the interface with mock response generated by Bogus library. I have added a line by line explanation of what is happening behind the scene.
Now, we will execute the test cases.
[Test]
public void GetEmployeesTest() {
//Act
var actual = employeeBusinessLogic.GetEmployees();
//Asset
Assert.Multiple(() => {
Assert.IsNotNull(actual);
Assert.Positive(actual.Count());
});
}
Overall the Test Case class looks like this,
using Bogus;
using BusinessLogicLayer;
using DataAccessLayer;
using Entities;
using Moq;
using NUnit.Framework;
using System;
using System.Linq;
namespace NUnit.Test {
public class EmployeeBusinessLogicUnitTests {
private IEmployeeBusinessLogic employeeBusinessLogic;
[SetUp]
public void Setup() {
//Arrange
//Set Id to 0 that will be increased on every iteration by faker object id rule
int id = 0;
//Initialize the mock class and test employees class object
var dataAccessMock = new Mock < IEmployeeDataAccess > ();
var testEmployees = new Faker < Employee > ();
//This contains the rules for generating the fake data.
//It takes 2 parameters, First is property name of employee object and second is replacement value
//The replacement value can be rule based or random data generated from Faker class, eg. f=>f.Person.FullName will generate a fake name
testEmployees.RuleFor(x => x.Id, ++id).RuleFor(x => x.Name, f => f.Person.FullName).RuleFor(x => x.Location, f => f.Address.State()).RuleFor(x => x.EmailId, f => f.Internet.Email()).RuleFor(x => x.EmployeeId, Guid.NewGuid());
//This will set the mock method with response generated from Bogus library
dataAccessMock.Setup(c => c.GetEmployees()).Returns(testEmployees.GenerateBetween(1, 20));
//This will assign the mock data access object to business logic constructor
employeeBusinessLogic = new EmployeeBusinessLogic(dataAccessMock.Object);
}
[Test]
public void GetEmployeesTest() {
//Act
var actual = employeeBusinessLogic.GetEmployees();
//Asset
Assert.Multiple(() => {
Assert.IsNotNull(actual);
Assert.Positive(actual.Count());
});
}
}
}
That’s it! Thanks for reading. I have uploaded sample code for reference. Please feel free to drop your comments.