How to write UNIT test with .Net 6 Framework in Clean Architecture with CQRS pattern

Create xUnit Test Project

Step 1:

Launch Visual Studio 2022 and Select “Create New Project”

Step 2:

Choose xUnit Test Project and press NEXT.

Step 3: Enter the Project name, and then click Create

Step 4:  Select .NET 6 and press Create button. Your xUnit test project will be created.

 

Install NuGet Packages

1. Right click on the project and select Manage NuGet Package

2. Install the following plugins

  • Moq
  • Shouldy

Creating Relevant Folders

Step 1: 

Right click on the project and Create a Folder named MockDatas .  In this folder all the property class will be created.  We are creating MockDatas for testing our projects function. Here i already a module named LoanOfficer. Our work is to test all the crud function inside the loan officer module.

Step 2:

Right click on the folder and create a class named LoanOfficerMockData and paste the following code in your class and change the property field according to yours.

using Products.Core.Entities;
using System;
using System.Collections.Generic;
namespace Products.Test.MockDatas
{
    public static class LoanOfficerMockData
    {
        public static List<LoanOfficer> loanofficerList = new List<LoanOfficer>();
        public static List<LoanOfficer> updateloanofficerList = new List<LoanOfficer>();
        public static List<LoanOfficer> deleteloanofficerList = new List<LoanOfficer>();
        public static List<LoanOfficer> GetLoanOfficersData()
        {
            return loanofficerList;
        }
        public static List<LoanOfficer> updateGetLoanOfficersData()
        {
            return updateloanofficerList;
        }
        public static List<LoanOfficer> deleteGetLoanOfficersData()
        {
            return deleteloanofficerList;
        }
        public static void AddLoanOfficerStatic()
        {
            loanofficerList.Add(new LoanOfficer
            {
                Id = 1,
                Name = "Anik",
                Code = 001,
                Designation = "ASAI"
            });
            loanofficerList.Add(new LoanOfficer
            {
                Id = 3,
                Name = "Ani",
                Code = 003,
                Designation = "ASAI"
            });
            loanofficerList.Add(new LoanOfficer
            {
                Id = 2,
                Name = "Anik Saha",
                Code = 002,
                Designation = "ASAI"
            });
        }
        public static void deleteAddLoanOfficerStatic()
        {
            deleteloanofficerList.Add(new LoanOfficer
            {
                Id = 1,
                Name = "Anik",
                Code = 001,
                Designation = "ASAI"
            });
            deleteloanofficerList.Add(new LoanOfficer
            {
                Id = 3,
                Name = "Ani",
                Code = 003,
                Designation = "ASAI"
            });
            deleteloanofficerList.Add(new LoanOfficer
            {
                Id = 2,
                Name = "Anik Saha",
                Code = 002,
                Designation = "ASAI"
            });
        }
        public static void updateAddLoanOfficerStatic()
        {
            updateloanofficerList.Add(new LoanOfficer
            {
                Id = 1,
                Name = "Anik",
                Code = 001,
                Designation = "ASAI"
            });
            updateloanofficerList.Add(new LoanOfficer
            {
                Id = 3,
                Name = "Ani",
                Code = 003,
                Designation = "ASAI"
            });
            updateloanofficerList.Add(new LoanOfficer
            {
                Id = 2,
                Name = "Anik Saha",
                Code = 002,
                Designation = "ASAI"
            });
        }
        public static void ResetLoanOfficerStatic()
        {
            loanofficerList.Clear();
        }
    }
}

Step 3:

Right click on the project and Create another Folder named MockRepositories. Here all the base function will be called and checked their return types are okay or not.

Step 4:

Right click on the folder and create a class named MockLoanOfficerRepository and paste the following code in your class and change the Setup functions according to yours
using Moq;
using Products.Application.Common.Interfaces.Repositories.Commands;
using Products.Application.Common.Interfaces.Repositories.Queries;
using Products.Core.Entities;
using Products.Test.MockDatas;
using System.Linq;
namespace Products.Test.MockRepositories
{
    public class MockLoanOfficerRepository
    {
        public MockLoanOfficerRepository()
        {
            LoanOfficerMockData.AddLoanOfficerStatic();
        }
        public static Mock<ILoanOfficerQueryRepository> GetAllLoanOfficers()
        {
            var loanOfficers = LoanOfficerMockData.GetLoanOfficersData();
            var mockRepo = new Mock<ILoanOfficerQueryRepository>();
            mockRepo.Setup(x => x.GetAllLoanOfficers(1, 1)).ReturnsAsync(loanOfficers);
            return mockRepo;
        }
        public static Mock<ILoanOfficerQueryRepository> GetAllLoanOfficersByID()
        {
            var loanOfficer = LoanOfficerMockData.GetLoanOfficersData().Where(x => x.Id == 1).FirstOrDefault();
            var mockRepo = new Mock<ILoanOfficerQueryRepository>();
            mockRepo.Setup(x => x.GetLoanOfficerById(1)).ReturnsAsync(loanOfficer);
            return mockRepo;
        }
        public static Mock<ILoanOfficerQueryRepository> DeleteLoanOfficersByID()
        {
            var loanOfficer = LoanOfficerMockData.deleteGetLoanOfficersData().Where(x => x.Id == 1).FirstOrDefault();
            var mockRepo = new Mock<ILoanOfficerQueryRepository>();
            mockRepo.Setup(x => x.GetLoanOfficerById(1)).ReturnsAsync(loanOfficer);
            return mockRepo;
        }
        public static Mock<ILoanOfficerQueryRepository> UpdateLoanOfficersByID()
        {
            var loanOfficer = LoanOfficerMockData.updateGetLoanOfficersData().Where(x => x.Id == 1).FirstOrDefault();
            var mockRepo = new Mock<ILoanOfficerQueryRepository>();
            mockRepo.Setup(x => x.GetLoanOfficerById(1)).ReturnsAsync(loanOfficer);
            return mockRepo;
        }
        public static Mock<ILoanOfficerCommandRepository> CreateLoanOfficers()
        {
            var loanOfficers = LoanOfficerMockData.GetLoanOfficersData();
            var mockRepoCreate = new Mock<ILoanOfficerCommandRepository>();
            mockRepoCreate.Setup(r => r.CreateLoanOfficerAsync(It.IsAny<LoanOfficer>())).ReturnsAsync((LoanOfficer loanOfficer) =>
            {
                LoanOfficerMockData.loanofficerList.Add(new LoanOfficer { });
                return loanOfficer;
            });
            return mockRepoCreate;
        }
        public static Mock<ILoanOfficerCommandRepository> DeleteLoanOfficer()
        {
            var loanOfficer = LoanOfficerMockData.GetLoanOfficersData().Where(x => x.Id == 1).FirstOrDefault();
            var mockRepo = new Mock<ILoanOfficerCommandRepository>();
            mockRepo.Setup(x => x.DeleteLoanOfficerAsync(loanOfficer));
            return mockRepo;
        }
        public static Mock<ILoanOfficerCommandRepository> UpdateLoanOfficer()
        {
            var loanOfficer = LoanOfficerMockData.GetLoanOfficersData().Where(x => x.Id == 1).FirstOrDefault();
            var mockRepo = new Mock<ILoanOfficerCommandRepository>();
            mockRepo.Setup(x => x.UpdateLoanOfficerAsync(loanOfficer));
            return mockRepo;
        }
    }
}

Step 5:

Right click on the project and Create another Folder named Systems. Inside Systems folder, create a folder in your module name. In my case I have created a folder named LoanOfficer. Inside LoanOfficer folder create two folder named “Commands “ and “Queries”. In “Commands ”  folder we will write test function for all Create, Edit and Delete and in “Queries” folder we will write test function for all List or Search functions.

Step 6:

Right click on the Queries folder and create a class GetLoanOfficerListRequestHandlerTests.cs and paste the following code in your class and change arrange, act and assert according to yours.

using AutoMapper;
using Moq;
using Products.Application.Common.Interfaces.Repositories.Queries;
using Products.Application.Common.Mappings;
using Products.Application.DTOs;
using Products.Application.Queries.LoanOfficer;
using Products.Test.MockDatas;
using Products.Test.MockRepositories;
using Shouldly;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Products.Test.Systems.LoanOfficer.Queries
{
    public class GetLoanOfficerListRequestHandlerTests
    {
        private readonly IMapper _mapper;
        private readonly Mock<ILoanOfficerQueryRepository> _mockRepo, _mockRepoById;
        private readonly GetLoanOfficersQueryRequest _loanOfficerRequest;
        private readonly GetLoanOfficerByIdQueryRequest _loanOfficerIdRequest;
        public GetLoanOfficerListRequestHandlerTests()
        {
            LoanOfficerMockData.AddLoanOfficerStatic();
            _mockRepo = MockLoanOfficerRepository.GetAllLoanOfficers();
            _mockRepoById = MockLoanOfficerRepository.GetAllLoanOfficersByID();
            var mapperConfig = new MapperConfiguration(c =>
            {
                c.AddProfile<MappingProfile>();
            });
            _mapper = mapperConfig.CreateMapper();
            _loanOfficerRequest = new GetLoanOfficersQueryRequest
            {
                Offset = 1,
                PageSize = 1,
            };
            _loanOfficerIdRequest = new GetLoanOfficerByIdQueryRequest(1);
        }
        [Fact]
        public async Task GetLoanOfficerList()
        {
            ///arrange
            var handler = new GetLoanOfficerQueryRequestHandler(_mockRepo.Object, _mapper);
            ///act
            var result = await handler.Handle(_loanOfficerRequest, CancellationToken.None);
            ///assert
            result.ShouldBeOfType<List<LoanOfficerResponseDto>>();
            result.Count().ShouldBe(3);
        }
        [Fact]
        public async Task GetLoanOfficerByID()
        {
            ///arrange
            var handler = new GetLoanOfficerByIdQueryRequestHandler(_mapper, _mockRepoById.Object);
            ///act
            var result = await handler.Handle(_loanOfficerIdRequest, CancellationToken.None);
            ///assert
            result.ShouldBeOfType<LoanOfficerResponseDto>();
            result.Id.ShouldBe(1);
        }
    }
}

Step 7: 

Now go to Test Explorer. Here you can see your class name. Expand this and right click n your function and press run.

if your functions passes all the criteria it will return pass and returns green sign. Thogh your mockdata contains three records so list is pass if you write following in the assert option.

 result.Count().ShouldBe(3);

Step 8:

Right click on the Commands folder and create a class named  CreateLoanOfficerCommandHandlerTest.cs and paste the following code in your class and change arrange, act and assert according to yours.

using AutoMapper;
using Moq;
using Products.Application.Commands.LoanOfficer;
using Products.Application.Common.Interfaces.Repositories;
using Products.Application.Common.Interfaces.Repositories.Commands;
using Products.Application.Common.Mappings;
using Products.Application.DTOs;
using Products.Test.MockDatas;
using Products.Test.MockRepositories;
using Shouldly;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Products.Test.Systems.LoanOfficer.Commands
{
    public class CreateLoanOfficerCommandHandlerTest
    {
        private readonly IMapper _mapper;
        private readonly Mock<ILoanOfficerCommandRepository> _mockRepo;
        private readonly IUnitOfWork _unitOfWork;
        private readonly CreateLoanOfficerCommandRequest _loanOfficerResponseDTO;
        public CreateLoanOfficerCommandHandlerTest()
        {
            _mockRepo = MockLoanOfficerRepository.CreateLoanOfficers();
            var mapperConfig = new MapperConfiguration(c =>
            {
                c.AddProfile<MappingProfile>();
            });
            _mapper = mapperConfig.CreateMapper();
            var mockUoW = new Mock<IUnitOfWork>();
            _unitOfWork = mockUoW.Object;
            _loanOfficerResponseDTO = new CreateLoanOfficerCommandRequest
            {
                Name = "ASAI",
                Code = 005,
                Designation = "SE"
            };
        }
        [Fact]
        public async Task CreateLoanOfficer()
        {
            ///arrange
            var handler = new CreateLoanOfficerCommandRequestHandler(_mockRepo.Object, _mapper, _unitOfWork);
            ///act
            var result = await handler.Handle(_loanOfficerResponseDTO, CancellationToken.None);
            LoanOfficerMockData.ResetLoanOfficerStatic();
            LoanOfficerMockData.AddLoanOfficerStatic();
            LoanOfficerMockData.loanofficerList.Add(new Products.Core.Entities.LoanOfficer
            {
                Name = "ASAI",
                Code = 005,
                Designation = "SE"
            });
            ///assert
            result.ShouldBeOfType<LoanOfficerResponseDto>();
            LoanOfficerMockData.loanofficerList.Count().ShouldBe(4);
        }
    }
}

Step 9: 

Run test like previous. But this time your data count should be 4 cause it will add a new LoanOfficer in LoanOfficerMockData. 

 LoanOfficerMockData.loanofficerList.Count().ShouldBe(4);

Step 10: 

Like this now create another class for Delete named DeleteLoanOfficerCommandHandlerTest.cs  and paste the following code.

using AutoMapper;
using Moq;
using Products.Application.Commands.LoanOfficer;
using Products.Application.Common.Interfaces.Repositories;
using Products.Application.Common.Interfaces.Repositories.Commands;
using Products.Application.Common.Interfaces.Repositories.Queries;
using Products.Application.Common.Mappings;
using Products.Test.MockDatas;
using Products.Test.MockRepositories;
using Shouldly;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Products.Test.Systems.LoanOfficer.Commands
{
    public class DeleteLoanOfficerCommandHandlerTest
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly Mock<ILoanOfficerCommandRepository> _mockRepo;
        private readonly Mock<ILoanOfficerQueryRepository> _mockRepoList;
        private readonly DeleteLoanOfficerCommandRequest _loanOfficerDeleteRequest;
        public DeleteLoanOfficerCommandHandlerTest()
        {
            LoanOfficerMockData.AddLoanOfficerStatic();
            _mockRepoList = MockLoanOfficerRepository.GetAllLoanOfficersByID();
            _mockRepo = MockLoanOfficerRepository.CreateLoanOfficers();
            LoanOfficerMockData.deleteAddLoanOfficerStatic();
         
            _mockRepoList = MockLoanOfficerRepository.DeleteLoanOfficersByID();
            _mockRepo = MockLoanOfficerRepository.DeleteLoanOfficer();
            var mockUoW = new Mock<IUnitOfWork>();
            _unitOfWork = mockUoW.Object;
            _loanOfficerDeleteRequest = new DeleteLoanOfficerCommandRequest(1);
        }
        [Fact]
        public async Task DeleteLoanOfficer()
        {
            ///arrange
              var handler = new DeleteLoanOfficerCommandRequestHandler(_mockRepoList.Object, _mockRepo.Object, _unitOfWork);
            
           ///act
            var result = await handler.Handle(_loanOfficerDeleteRequest, CancellationToken.None);
            var loanOfficer = LoanOfficerMockData.deleteGetLoanOfficersData().Where(x => x.Id == 1).FirstOrDefault();
            LoanOfficerMockData.deleteloanofficerList.Remove(loanOfficer);
           
           ///assert
            result.ShouldBeOfType<int>();
            LoanOfficerMockData.deleteloanofficerList.Count().ShouldBe(2);
        }
    }
}

Step 11:

Run test like previous. But this time your data count should be 2 cause it will delete a LoanOfficer in LoanOfficerMockData. 

LoanOfficerMockData.deleteloanofficerList.Count().ShouldBe(2);

Step 12:

Like this now create another class for Update named UpdateLoanOfficerCommandHandlerTest.cs  and paste the following code.

using AutoMapper;
using Moq;
using Products.Application.Commands.LoanOfficer;
using Products.Application.Common.Interfaces.Repositories;
using Products.Application.Common.Interfaces.Repositories.Commands;
using Products.Application.Common.Interfaces.Repositories.Queries;
using Products.Application.Common.Mappings;
using Products.Application.DTOs;
using Products.Test.MockDatas;
using Products.Test.MockRepositories;
using Shouldly;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Products.Test.Systems.LoanOfficer.Commands
{
    public class UpdateLoanOfficerCommandHandlerTest
    {
        private readonly IMapper _mapper;
        private readonly IUnitOfWork _unitOfWork;
        private readonly Mock<ILoanOfficerCommandRepository> _mockRepo;
        private readonly Mock<ILoanOfficerQueryRepository> _mockRepoList;
        private readonly UpdateLoanOfficerCommandRequest _loanOfficerUpdateRequest;
        public UpdateLoanOfficerCommandHandlerTest()
        {
            LoanOfficerMockData.updateAddLoanOfficerStatic();
            _mockRepo = MockLoanOfficerRepository.UpdateLoanOfficer();
            _mockRepoList = MockLoanOfficerRepository.UpdateLoanOfficersByID();
            var mapperConfig = new MapperConfiguration(c =>
            {
                c.AddProfile<MappingProfile>();
            });
            _mapper = mapperConfig.CreateMapper();
            var mockUoW = new Mock<IUnitOfWork>();
            _unitOfWork = mockUoW.Object;
            _loanOfficerUpdateRequest = new UpdateLoanOfficerCommandRequest
            {
                Id = 1,
                Name = "ASA International",
                Designation = "SE"
            };
        }
        [Fact]
        public async Task UpdateLoanOfficer()
        {
            ///arrange
            var handler = new UpdateLoanOfficerCommandRequestHandler(_mapper, _mockRepo.Object, _mockRepoList.Object, _unitOfWork);
            ///act
            var result = await handler.Handle(_loanOfficerUpdateRequest, CancellationToken.None);
            var loanOfficer = LoanOfficerMockData.updateloanofficerList.FirstOrDefault(x => x.Id == 1);
            loanOfficer.Name = "ASA International";
            ///assert
            result.ShouldBeOfType<LoanOfficerResponseDto>();
        }
    }
}

All Test cases are ready and go to GetLoanOfficerListRequestHandlerTests.cs class again and change result.Count().ShouldBe(4) in GetLoanOfficerList() function. Beacuse if we run all the test functions simultaniously, then CreateLoanOfficer() function will add a dataset. So GetLoanOfficerList() count should be 4 if we already have 3 dataset in MockLoanOfficerRepository.

Now go to Test Explorer . Right Click on the project name and press Run. If your all functions are okay it will pass else it will return what is wrong.

 

 

Leave a Reply