Unit test quick book C#, .Net Core, Moq, xUnit
Some quick notes about writing unit tests using C#, .Net Core, moq, xUnit.
You can refer to some best practices in link in reference section
Simple Test
This is a very basic test case sample
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add("");
// Assert
Assert.Equal(0, actual);
}
Show Name on Test
If you want to overwrite the test name with a meaningful formatted test, you can use the below
[Fact(DisplayName = "Metod or group : Should creitea with response")]
Mapper to Test
In most case you will be using a mapper inside your application and you may have initialized them inside your startup class, to assign them in your testing, you can use the same profile or a duplicated profile and assign them in your test. you can also use them in a separate test utility file so that you can re share the same between tests.
public static class TestUtils
{
public static Mapper GetMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(typeof(MappingProfileConfiguration));
});
return new Mapper(config);
}
}
var mapper = TestUtils.GetMapper();
Mock Configuration to Test
In most cases we will be using configurations with in our applications, to test the same we can use the below mocked functionality to test based on your need
public static IConfiguration GetMockConfiguration()
{
var inMemorySettings = new Dictionary<string, string> {
{"SampleKey1", "SampleValue1"},
{"SampleKey2", "SampleValue2"}
};
return new ConfigurationBuilder()
.AddInMemoryCollection(inMemorySettings)
.Build();
}
Mocked HTTP Context
In Some cases you may need to use the HTTP Context values while testing, like a bearer token or something, for those kind of tests, you can use this mock functionality which will set a set of dummy key values to the HTTP Context and return the context for you test
public static HttpContext GetHttpContext(List<(string, string)> headerArray)
{
var claims = new List<Claim>();
foreach (var item in headerArray)
{
claims.Add(new Claim(item.Item1, item.Item2));
}
var identity = new ClaimsIdentity(claims, "TestAuthType");
var claimsPrincipal = new ClaimsPrincipal(identity);
return new DefaultHttpContext()
{
User = claimsPrincipal
};
}
you can set them like below
var mockContext = new Mock<IHttpContextAccessor>();
mockContext.SetupGet(x => x.HttpContext)
.Returns(TestUtils.GetHttpContext(
new List<(string, string)> {
("SampleKey1", "SampleValue1"),
("SampleKey2", "SampleValue2") }));
Mocking a repository with dummy value
mocking a simple repository with some dummy values
mockRepo.Setup(x => x.QuerySingleAsync(
It.IsAny<Expression<Func<yourDto, bool>>>()))
.ReturnsAsync(new yourDto
{
yourDto_Id = 20001,
yourDto_Prop1 = 18,
yourDto_Prop2 = 30
});
Mocking a repository with null value
mocking with a null value, to test the failure cases
yourDto nullDto = null;
mockRepo.Setup(x => x.QuerySingleAsync(
It.IsAny<Expression<Func<yourDto, bool>>>()))
.ReturnsAsync(nullDto);
Mocking a repository with multiple invocations
Incase of multiple invocations to the function you have to check the same in sequence like below
mockRepo.SetupSequence(x => x.QuerySingleAsync(
It.IsAny<Expression<Func<yourDto, bool>>>()))
.ReturnsAsync(new yourDto //first response
{
yourDto_Id = 20001,
yourDto_Prop1 = 18,
yourDto_Prop2 = 30
})
.ReturnsAsync(new yourDto //second respnse
{
yourDto_Id = 20001,
yourDto_Prop1 = 18,
yourDto_Prop2 = 30
});
Mocking Repository and check the received object
Incase of saving an object to repo, you may need to check the object while asserting the data, you can use below sample for the same
yourDto saveObject = null;
mockRepo.Setup(x => x.CreateAsync(It.IsAny<yourDto>()))
.Callback<yourDto>((obj) => saveObject = obj)
.ReturnsAsync((true, 1))
Assert.NotNull(saveObject);
Assert the mock service executed count
In some cases you may need to check the number of times the mocked services executed, for those you can use below
mockRepo.Verify(x => x.CreateAsync(It.IsAny<yourDto>()), Times.Once());
Mock Exceptions
In some cases you may need to test exceptions, use below snippet for the same
mockRepo.Setup(x => x.CreateAsync(It.IsAny<yourDto>()))
.Throws(new ArgumentNullException());
Test Multiple Inputs
[Theory]
[InlneData(1,2,3)]
[InlneData(2,2,4)]
public void Add_ShoulddWorkWithMultipleInputs(int item1, int item2, int output)
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add(item1, item2);
// Assert
Assert.Equal(output, actual);
}
Test Multiple Objects
previous example will show the testing for minimal number of parameters for unit testing, but if its an object with multiple properties, then it will be hard to provide them inline, for that we can use below
[Theory]
[MemberData(nameof(GetTestData))]
public void Add_ShoulddWorkWithMultipleInputs(youtDto)
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add(youtDto.Item1, youtDto.Item2);
// Assert
Assert.Equal(youtDto.Output, actual);
}
public static IEnumerable<youtDto> GetTestData()
{
yield return new youtDto { Item1 = 1, Item1 = 2, Output = 3};
yield return new youtDto { Item1 = 2, Item1 = 2, Output = 4};
}
Load IFormFile in testing
Loading file as part of testing, this will use some dummy files in your test file and load the same while testing
var stream = File.OpenRead(filePath);
IFormFile file = new FormFile(stream, 0, stream.Length, "files", Path.GetFileName(filePath))
{
Headers = new HeaderDictionary(),
ContentType = (filePath.EndsWith("jpg") || filePath.Split('.')[1] == "jpeg") ? "image/jpeg"
: filePath.EndsWith("png") ? "image/png"
: "image/bmp",
};
These are some of the useful snippets for your unit test, will keep updating with more, keep coding.. :)
Reference