Skip to main content

# Unit testing

With unit tests you usually test individual service methods, by defining the outputs.

For unit tests we will use `xUnit`, `NSubsitute` and `FluentAssertions`. This is a personal prefference, you can use the libraries that fit best to your team

- create a new xUnit project inside the Tests folder in your started solution
- Call it:`SpeakerService.Tests.Unit`
- add a file named `SpeakerServiceTests`
- Install the NuGet packages: `FluentAssertions`, `NSubstitute`

Setting the ground

            private readonly ISpeakerRepository speakersRepository;
            private readonly ISpeakerService sut;
            private ILogger<Speaker.Service.SpeakerService> logger;
            private IMapper mapper;

            public SpeakerServiceTests()
            {
                this.speakersRepository = Substitute.For<ISpeakerRepository>(); ;
                logger = Substitute.For<ILogger<Speaker.Service.SpeakerService>>();

                if (mapper == null)
                {
                    var mappingConfig = new MapperConfiguration(mc =>
                    {
                        mc.AddProfile(new SpeakerMappingProfile());
                    });
                    IMapper mapper = mappingConfig.CreateMapper();
                    this.mapper = mapper;
                }
                this.sut = new Speaker.Service.SpeakerService(logger, speakersRepository, mapper);
            }

- Create a folder named `Helpers`
- add a class named `TestServerCallContext`
- paste the code below

TestServerCallContext.cs

        public class TestServerCallContext : ServerCallContext
        {
            private readonly Metadata _requestHeaders;
            private readonly CancellationToken _cancellationToken;
            private readonly Metadata _responseTrailers;
            private readonly AuthContext _authContext;
            private readonly Dictionary<object, object> _userState;
            private WriteOptions? _writeOptions;

            public Metadata? ResponseHeaders { get; private set; }

            private TestServerCallContext(Metadata requestHeaders, CancellationToken cancellationToken)
            {
                _requestHeaders = requestHeaders;
                _cancellationToken = cancellationToken;
                _responseTrailers = new Metadata();
                _authContext = new AuthContext(string.Empty, new Dictionary<string, List<AuthProperty>>());
                _userState = new Dictionary<object, object>();
            }

            protected override string MethodCore => "MethodName";
            protected override string HostCore => "HostName";
            protected override string PeerCore => "PeerName";
            protected override DateTime DeadlineCore { get; }
            protected override Metadata RequestHeadersCore => _requestHeaders;
            protected override CancellationToken CancellationTokenCore => _cancellationToken;
            protected override Metadata ResponseTrailersCore => _responseTrailers;
            protected override Status StatusCore { get; set; }
            protected override WriteOptions? WriteOptionsCore { get => _writeOptions; set { _writeOptions = value; } }
            protected override AuthContext AuthContextCore => _authContext;

            protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions? options)
            {
                throw new NotImplementedException();
            }

            protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders)
            {
                if (ResponseHeaders != null)
                {
                    throw new InvalidOperationException("Response headers have already been written.");
                }

                ResponseHeaders = responseHeaders;
                return Task.CompletedTask;
            }

            protected override IDictionary<object, object> UserStateCore => _userState;

            public static TestServerCallContext Create(Metadata? requestHeaders = null, CancellationToken cancellationToken = default)
            {
                return new TestServerCallContext(requestHeaders ?? new Metadata(), cancellationToken);
            }
        }

This class will make

Then we need to test our first method - `GetById`.

# Writing our first unit test

- add a method named `GetById_ShouldReturn_an_Object`

Method skeleton

           [Fact]
            public async Task GetById_ShouldReturn_an_Object()
            {

            }

- inside the class we will follow the AAA pattern as below

- Arrange( prepare the return objects, and make sure every line in the service will return what we need to cover the case)

Arrange

    // Arrange
                var callContext = TestServerCallContext.Create();
                var request = new SpeakerFilterRequest { Id = 4 };

                var speaker = new Domain.Speaker()
                {
                    Id = 4,
                    City = "Oslo",
                    Country = "Norway",
                    Email = "speaker@mail.com",
                    FirstName = "Dev",
                    LastName = "Speaker",
                    Website = "www.speaker.com"
                };

                speakersRepository.GetByIdAsync(Arg.Any<int>())
                   .Returns(speaker);

                var speakerResponse = new SpeakerResponse
                {
                    Id = 4,
                    City = "Bucharest",
                    Country = "Belgium",
                    Email = "speaker@mail.com",
                    FirstName = "Dev",
                    LastName = "Speaker",
                    Website = "www.speaker.com"
                };

- Act - call the actual service method that we want to test

     // Act
     var response = await sut.GetById(request, callContext);

- Assert what the expected result should be This section is where we use the (Expected result vs Actual result ) comparison

Assert

    // Assert
    response.Should().BeEquivalentTo(speakerResponse);

DON't open
Full implementation

    public class SpeakerServiceTests
        {

            private readonly ISpeakerRepository speakersRepository;
            private readonly ISpeakerService sut;
            private ILogger<Speaker.Service.SpeakerService> logger;
            private IMapper mapper;

            public SpeakerServiceTests()
            {
                this.speakersRepository = Substitute.For<ISpeakerRepository>(); ;
                logger = Substitute.For<ILogger<Speaker.Service.SpeakerService>>();

                if (mapper == null)
                {
                    var mappingConfig = new MapperConfiguration(mc =>
                    {
                        mc.AddProfile(new SpeakerMappingProfile());
                    });
                    IMapper mapper = mappingConfig.CreateMapper();
                    this.mapper = mapper;
                }
                this.sut = new Speaker.Service.SpeakerService(logger, speakersRepository, mapper);
            }

            [Fact]
            public async Task GetById_ShouldReturn_an_Object()
            {
                // Arrange
                var callContext = TestServerCallContext.Create();
                var request = new SpeakerFilterRequest { Id = 4 };

                var speaker = new Domain.Speaker()
                {
                    Id = 4,
                    City = "Bucharest",
                    Country = "Belgium",
                    Email = "speaker@mail.com",
                    FirstName = "Dev",
                    LastName = "Speaker",
                    Website = "www.speaker.com"
                };

                speakersRepository.GetByIdAsync(Arg.Any<int>())
                   .Returns(speaker);

                var speakerResponse = new SpeakerResponse
                {
                    Id = 4,
                    City = "Bucharest",
                    Country = "Belgium",
                    Email = "speaker@mail.com",
                    FirstName = "Dev",
                    LastName = "Speaker",
                    Website = "www.speaker.com"
                };

                // Act
                var response = await sut.GetById(request, callContext);

                // Assert
                response.Should().BeEquivalentTo(speakerResponse);

            }

        }