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 = "[email protected]",
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 = "[email protected]",
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 = "[email protected]",
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 = "[email protected]",
FirstName = "Dev",
LastName = "Speaker",
Website = "www.speaker.com"
};

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

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

}

}