Creating Unit test Layer

In this session we will create the unit test using xUnit .net core.

Here in this project we will not create 100% test coverage, the idea is to show the test applied to the most important parts of the system that already make up much of our small project. Always remembering that we owe the test standards defined by convention as method naming and directory organization. Let's start by implementing the tests for our business layer. Following the pattern we will create the directories according to the project we are testing to facilitate understanding of the test applied to our project.


Note that we create the exact path of the application we want to test on and by convention our test file is called exactly the same as the file we are going to test just by adding tests to the end. And finally we will create the tests.

As we saw earlier, you must install the dependencies we will use here


Unit tests should only test each method of your system individually and should never access anything external such as database, read or save files, make requests or anything like that, this is the function of an integration test and we are only testing the method. implemented ie by performing unit tests. For that we will use 'Mock' to create mock of our repository simulating a database operation. The name of the method we are going to test on should be the same name following what will happen on the test and then returning it. Making it clear what will happen in your test.


Now let's implement our method. The test should always be simple. There should be no loop loops and no decision structures, if you have this need in your test, this means that you need to refactor your code to make it testable. The method is divided into 3 phases. Arrange phase, Processing phase and Assert phase. Always remembering that our testable methods should be annotated "[Fact]" so that visual studio at the time of testing will identify these methods as test methods. Below is an example of our first implemented method.

              
                using AutoMapper;
                using Moq;
                using Rodolfo.Schmidt.Application.Dto;
                using Rodolfo.Schmidt.Application.Mapping;
                using Rodolfo.Schmidt.Application.Services;
                using Rodolfo.Schmidt.Domain.Entities;
                using Rodolfo.Schmidt.Domain.Interfaces.Repositories;
                using Rodolfo.Schmidt.Domain.Interfaces.UoW;
                using System;
                using System.Collections.Generic;
                using System.Linq.Expressions;
                using System.Text;
                using System.Threading.Tasks;
                using Xunit;
                
                namespace Rodolfo.Schmidt.Tests.Rodolfo.Schmidt.Application.Services
                {
                    public class PersonServiceTests
                    {
                        private Mock<IUnitOfWork> _mockUnitOfWork;
                        private Mock<IPersonRepository> _mockPersonRepository;
                        private IMapper _mapper;
                
                        public PersonServiceTests()
                        {
                            _mockUnitOfWork = new Mock<IUnitOfWork>();
                            _mockPersonRepository = new Mock<IPersonRepository>();
                            var mappingProfile = new MappingProfile();
                
                            var config = new MapperConfiguration(mappingProfile);
                            _mapper = new Mapper(config);
                        }
                
                        [Fact]
                        public async Task CreatePerson_CreatingPerson_ReturningSuccessMessageAndTrue()
                        {
                            // arrange phase
                            var person = new PersonDto { Age = 30, Name = "Rodolfo Schmidt" };
                            _mockUnitOfWork.Setup(x => x.CommitAsync()).ReturnsAsync(true);
                
                            // processing phase
                            var service = new PersonService(_mapper, _mockPersonRepository.Object, _mockUnitOfWork.Object);
                
                            var result = await service.CreatePerson(person);
                
                            // assert phase
                            Assert.True(result.Saved);
                            Assert.Equal("Person has been created successfuly", result.Message);
                        }
                
                    }
                }
                
              
            

You can and MUST. Create various test scenarios for this method. both test scenarios with ok flow and test scenarios following a flow and ending wrong. Let's not go into the merits here of exploring in detail xUnit and its features in their entirety, not in this particular session. But below I will bring an example of each for the methods of our business layer.

              
                using AutoMapper;
using Moq;
using Rodolfo.Schmidt.Application.Dto;
using Rodolfo.Schmidt.Application.Mapping;
using Rodolfo.Schmidt.Application.Services;
using Rodolfo.Schmidt.Domain.Entities;
using Rodolfo.Schmidt.Domain.Interfaces.Repositories;
using Rodolfo.Schmidt.Domain.Interfaces.UoW;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Rodolfo.Schmidt.Tests.Rodolfo.Schmidt.Application.Services
{
    public class PersonServiceTests
    {
        private Mock<IUnitOfWork> _mockUnitOfWork;
        private Mock<IPersonRepository> _mockPersonRepository;
        private IMapper _mapper;

        public PersonServiceTests()
        {
            _mockUnitOfWork = new Mock<IUnitOfWork>();
            _mockPersonRepository = new Mock<IPersonRepository>();
            var mappingProfile = new MappingProfile();

            var config = new MapperConfiguration(mappingProfile);
            _mapper = new Mapper(config);
        }

        [Fact]
        public async Task CreatePerson_CreatingPerson_ReturningSuccessMessageAndTrue()
        {
            // arrange phase
            var person = new PersonDto { Age = 30, Name = "Rodolfo Schmidt" };
            _mockUnitOfWork.Setup(x => x.CommitAsync()).ReturnsAsync(true);

            // processing phase
            var service = new PersonService(_mapper, _mockPersonRepository.Object, _mockUnitOfWork.Object);

            var result = await service.CreatePerson(person);

            // assert phase
            Assert.True(result.Saved);
            Assert.Equal("Person has been created successfuly", result.Message);
        }

        [Fact]
        public async Task DeletePerson_DeletingPerson_ReturningSuccessMessageAndTrue()
        {
            // arrange phase
            _mockUnitOfWork.Setup(x => x.CommitAsync()).ReturnsAsync(true);

            // processing phase
            var service = new PersonService(_mapper, _mockPersonRepository.Object, _mockUnitOfWork.Object);

            var result = await service.DeletePerson(3);

            // assert phase
            Assert.True(result.Deleted);
            Assert.Equal("Person has been deleted successfuly", result.Message);
        }
        
        [Fact]
        public async Task GetPeople_GettingPeople_ReturningPeopleList()
        {
            // arrange phase
            _mockPersonRepository.Setup(x => x.GetAll()).ReturnsAsync(new List<Person> { new Person { Id = 1, Age = 30, Name = "Rodolfo Schmidt" } });

            // processing phase
            var service = new PersonService(_mapper, _mockPersonRepository.Object, _mockUnitOfWork.Object);

            var result = await service.GetPeople();

            // assert phase
            Assert.NotEmpty(result);
            Assert.IsType<List<Person>>(result);
        }

        [Fact]
        public async Task GetPersonById_GettingPerson_ReturningPerson()
        {
            // arrange phase
            _mockPersonRepository.Setup(x => x.GetById(It.IsAny<int>())).ReturnsAsync(new Person { Id = 1, Age = 30, Name = "Rodolfo Schmidt" });

            // processing phase
            var service = new PersonService(_mapper, _mockPersonRepository.Object, _mockUnitOfWork.Object);

            var result = await service.GetPersonById(1);

            // assert phase
            Assert.NotNull(result);
            Assert.Equal("Rodolfo Schmidt", result.Name);
            Assert.IsType<Person>(result);
        }

        [Fact]
        public async Task UpdatePerson_UpdatingPerson_ReturningSuccessMessageAndTrue()
        {
            // arrange phase
            _mockPersonRepository.Setup(x => x.GetById(It.IsAny<int>())).ReturnsAsync(new Person { Id = 1, Age = 30, Name = "Rodolfo Schmidt" });
            _mockUnitOfWork.Setup(x => x.CommitAsync()).ReturnsAsync(true);
            var person = new PersonDto { Age = 30, Name = "Rodolfo Schurhaus Schmidt" };

            // processing phase
            var service = new PersonService(_mapper, _mockPersonRepository.Object, _mockUnitOfWork.Object);

            var result = await service.UpdatePerson(person);

            // assert phase
            Assert.True(result.Updated);
            Assert.Equal("Rodolfo Schurhaus Schmidt", result.person.Name);
            Assert.Equal("Person has been updated successfuly", result.Message);
        }
    }
}

              
            

By implementing the Update method you will realize the need for a business layer improvement by adapting the return of the updated object so that we can test the change in the object, make the appropriate changes to the update method and its interface as below.



Note that in the test method we are validating the change in the name of the person by simulating the database return with Mock and entering the user we want to change.


And finally let's run our test as below and finish the test session. To do this click on the left tab of the visual studio for tests and if it does not exist yet you can find it in the top menu Test> Windows> Test Explorer.


Remembering that you have access to the project in my repository here