Asp.Net Core 2.0 Web Api Unit Testing
There exist many kind of tests: unit tests, integration tests, acceptance test, UI tests.
The goal of this tutorial is to show how to write unit tests that gives us a better return on test investment.
UNIT TEST: Unit testing test behavior of a class and drive its implementation, run in memory and mocks the Data Access Layer
INTEGRATION TEST: test the entire system and focus on interactions between many classes, uses the real database and test the Data Access Layer and its value is to catch regressions
SUBCUTANEOUS TEST: test that operates just under the UI of an application. This is particularly valuable when doing functional testing of an application: when you want to test end-to-end behavior, but it’s difficult to test through the UI itself. Martin Fowler ( https://martinfowler.com/bliki/SubcutaneousTest.html)
UI TEST: tests the user interface and verifies that the entire application is working properly
Let’s consider the following testing pyramid

The further we go to the top of the pyramid
- tests are fewer
- the tests are less stable
- More complex
- Narrower scope
- Slower in execution time
The further we go down the pyramid
- Less we have tests
- the tests are more stable,
- Less complex
- Broader reach
- Faster in execution times
Let us consider the following project dependencies :

For the goal of this tutorial, I will not write unit test of all my projects even if all projects must be unit tested but I will proceed as follow:
- Unit Test my business Projet: LogCorner.BlogPost.Business
- Integration Test of my WebAPI project: LogCorner.BlogPost.Api
- UI Test of my web application : LogCorner.BlogPost.Mvc
Lets talk about Unit Testing TDD or BDD?
BDD approach: in absolute terms, TDD plus natural language expression using Gherkin.
Using TDD approach, we must write the tests first using the red-green-refactor cycle.
RED: Write the test first, it will fail
GREEN: Write tested code as simple as possible to make it pass
REFACTOR: Refactor your code; your test may fail, so make it pass again
But if you have already a production code, how to test it?
public class BlogService : IBlogService
{
private readonly IUnitOfWorkCore<LogcornerBlogpostContext> _unitOfWork;
public BlogService(IUnitOfWorkCore<LogcornerBlogpostContext> unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task UpdateAsync(Blog blog, string email)
{
if (blog == null || string.IsNullOrWhiteSpace(email))
{
throw new ArgumentNullException();
}
var old = await GetAsync(blog.BlogId);
if (old == null)
{
throw new Exception($"blog : {blog.BlogId} must existe in database");
}
if (old.Owner != email)
{
throw new AuthenticationException($"You are not authorized to update the blog of someone else : {blog.BlogId}");
}
old.Url = blog.Url;
old.Description = blog.Description;
_unitOfWork.GetRepository<Blog>().Update(old);
await _unitOfWork.SaveAsync();
}
public async Task CreateAsync(Blog blog, string owner)
{
if (blog == null || string.IsNullOrWhiteSpace(owner))
{
throw new ArgumentNullException();
}
blog.Owner = owner;
_unitOfWork.GetRepository<Blog>().Create(blog);
await _unitOfWork.SaveAsync();
}
public async Task<List<Blog>> GetAsync()
{
var result = await _unitOfWork.GetRepository<Blog>().All().ToListAsync();
return result;
}
public async Task<Blog> GetAsync(int blogId)
{
return await _unitOfWork.GetRepository<Blog>().GetByIdAsync(blogId);
}
public async Task<bool> OwnedByAsync(int id, string owner)
{
var blog = await _unitOfWork.GetRepository<Blog>().GetByIdAsync(id);
if (blog == null)
{
return false;
}
return blog.Owner == owner;
}
public async Task DeleteAsync(int blogId, string email)
{
if (blogId <= 0)
{
throw new ArgumentNullException(nameof(blogId), $"blog : {blogId} cannot be null");
}
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentNullException(nameof(email), $"email : {email} cannot be null");
}
var old = await GetAsync(blogId);
if (old == null)
{
throw new Exception($"blog : {blogId} cannot be null");
}
if (old.Owner != email)
{
throw new AuthenticationException($"You are authorized to update the blog : {blogId}");
}
_unitOfWork.GetRepository<Blog>().Delete(blogId);
await _unitOfWork.SaveAsync();
}
}
Given the previous class, I would like to unit test my UpdateAsync method.
To update a blog post, I must verify this:
- You must provide a non nullable blog post objet and your email
- The blog post , you want to update must exist on database
- You must be owner of the blog post you want to update
- The system must replace blog post description and url, with the values provider by the user
- The system must call GetRepository<Blog>().Update(old) to perform update
- The system must call SaveAsync() to save updated blog post
TEST 1 : To update a blog post, the user must provide a non nullable blog post objet and his email adress
Here, I verify, if the user provide a null blog or an empty or null email address then an ArgumentNullException is thrown
[Theory(DisplayName = "To update a blog post you must provide a valid blog object and a userName")]
[ClassData(typeof(BadRequestBlogTestData))]
public async Task UpdateBlogTestWitheInvalidParameters(Blog blog, string userName)
{
await Assert.ThrowsAsync<ArgumentNullException>(() => _blogService.UpdateAsync(blog, userName));
}
public class BadRequestBlogTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { null, null };
yield return new object[] { null, string.Empty };
yield return new object[] { null, "jean.dupont" };
yield return new object[] { new Blog(), null };
yield return new object[] { new Blog(), string.Empty };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
TEST 2: The blog post , you want to update must exist on database
I want to test the following:
I call GetAsync to retrieve the blog post by Id and I verify if the result is null or not
As you can see GetAsync make a call to _unitOfWork.GetRepository<Blog>().GetByIdAsync(blogId);
So, I must mock the call of GetByIdAsync(blogId); to return an existing blogpost.
Here I mock IUnitOfWorkCore<LogcornerBlogpostContext> because my blogService need it and I don’t want to use the real implementation of my repository.
Do not mock unitOfWork, means I’m doing integration testing but not unit testing.
Let’s go ahead and write the test
[Theory(DisplayName = "To update a blog post you must provide an existing blogId")]
[ClassData(typeof(UpdateBlogTestData))]
public async Task UpdateBlogTestWithNoExistingBlogThrowsException(Blog blog, string userName)
{
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().GetByIdAsync(It.IsAny<int>())).Returns(Task.FromResult((Blog)null)).Verifiable();
await Assert.ThrowsAsync<Exception>(() => _blogService.UpdateAsync(blog, userName));
_unitOfWorkMock.Verify(r => r.GetRepository<Blog>().GetByIdAsync(blog.BlogId));
}
public class UpdateBlogTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new Blog
{
BlogId = 0,
Url = "http://www.jean.dupont.com",
Description = "lorem"
}, "jean.dupont" };
yield return new object[] { new Blog
{
BlogId = -1,
Url = "http://www.jean.dupont.com",
Description = "lorem"
}, "jean.dupont" };
yield return new object[] { new Blog
{
BlogId = 18,
Url = "http://www.jean.dupont.com",
Description = "lorem"
}, "jean.dupont" };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
I verify, if the blog does not exist in database, then an Exception is thrown
TEST 3 : To update a blog post , you must be owner of that blog post : you cannot update a blog post of someone else
So, I want to test the following:
if (old.Owner != email)
{
throw new AuthenticationException($"You are not authorized to update the blog of someone else : {blog.BlogId}");
}
I test if the owner field of the blog I want to update is different from the current user email, if so the system throws an exception.
Let’s go ahead and test it
[Theory(DisplayName = "When No Owner Of Blog Update Blog Must Throws Authentication Exception")]
[ClassData(typeof(UpdateBlogNotOwnerTestData))]
public async Task WhenNoOwnerOfBlogUpdateBlogMustThrowsAuthenticationException(Blog blog, string userName)
{
var outputBlog = new Blog
{
BlogId = 1,
Url = "http://www.jean.dupont.com",
Description = "lorem",
Owner = "mathias.fererra"
};
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().GetByIdAsync(It.IsAny<int>())).Returns(Task.FromResult(outputBlog));
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().Update(blog)).Verifiable();
await Assert.ThrowsAsync<AuthenticationException>(() => _blogService.UpdateAsync(blog, userName));
_unitOfWorkMock.Verify(r => r.GetRepository<Blog>().GetByIdAsync(blog.BlogId));
Assert.NotEqual(blog.Owner, userName);
}
public class UpdateBlogNotOwnerTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new Blog
{
BlogId = 1,
Url = "http://www.jean.dupont.com",
Description = "lorem",
Owner = "mathias.fererra"
}, "jean.dupont"};
yield return new object[] { new Blog
{
BlogId = 2,
Url = "http://www.jean.dupont.com",
Description = "lorem",
Owner ="yves.kerghal"
}, "jean.dupont" };
yield return new object[] { new Blog
{
BlogId = 3,
Url = "http://www.jean.dupont.com",
Description = "lorem",
Owner ="marc.laplace"
},"jean.dupont" };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
TEST 4: To update a blog post, you must set the url and description of the blog post you want to update with new values of url and description
In this test I would like to verify the following
old.Url = blog.Url;
old.Description = blog.Description;
[Theory(DisplayName = "When owner of a given Blog update it with new values of URL and Description then the URL and Description of the blog must be ovverriden with new values")]
[ClassData(typeof(UpdateBlogOwnerTestData))]
public async Task WhenOwnerOfBlogUpdateBlogUrlAndDescriptionMustBeSetWithNewValues(Blog blog, string userName)
{
var blogToUpdate = new Blog
{
BlogId = 1,
Url = "http://www.jean.dupont.com",
Description = "lorem",
Owner = "jean.dupont"
};
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().GetByIdAsync(It.IsAny<int>())).Returns(Task.FromResult(blogToUpdate)).Verifiable();
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().Update(blog)).Verifiable();
await _blogService.UpdateAsync(blog, userName);
_unitOfWorkMock.Verify(r => r.GetRepository<Blog>().GetByIdAsync(blog.BlogId), Times.Once);
_unitOfWorkMock.Verify(r => r.GetRepository<Blog>().Update(blogToUpdate), Times.Once);
_unitOfWorkMock.Verify(m => m.GetRepository<Blog>().Update(It.Is<Blog>(n =>
n.Url.Equals(blog.Url, StringComparison.InvariantCultureIgnoreCase)
&& n.Description.Equals(blog.Description, StringComparison.InvariantCultureIgnoreCase)
&& n.BlogId.Equals(blogToUpdate.BlogId)
&& n.Owner.Equals(blogToUpdate.Owner, StringComparison.InvariantCultureIgnoreCase)
)));
}
public class UpdateBlogOwnerTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[]
{
new Blog
{
BlogId = 1,
Url = "http://www.new1.jean.dupont.com",
Description = "new lorem 1",
Owner = "jean.dupont"
},
"jean.dupont"
};
yield return new object[] {
new Blog
{
BlogId = 2,
Url = "http://www.new2.jean.dupont.com",
Description = "new lorem 2",
Owner ="jean.dupont"
},
"jean.dupont"
};
yield return new object[] {
new Blog
{
BlogId = 3,
Url = "http://www.new3.jean.dupont.com",
Description = "new lorem 3",
Owner ="jean.dupont"
},
"jean.dupont"
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
I use a Verify call to check if the url and the description of the old blog are updated with new values
_unitOfWorkMock.Verify(m => m.GetRepository<Blog>().Update(It.Is<Blog>(n =>
n.Url.Equals(blog.Url, StringComparison.InvariantCultureIgnoreCase)
&& n.Description.Equals(blog.Description, StringComparison.InvariantCultureIgnoreCase)
&& n.BlogId.Equals(blogToUpdate.BlogId)
&& n.Owner.Equals(blogToUpdate.Owner, StringComparison.InvariantCultureIgnoreCase)
)));
TEST 5 : When the owner of a given blog try to uptade it , then the blog must be updated
[Theory(DisplayName = "When owner of a given blog try to update it then the corresponding Blog is successfully updated")]
[ClassData(typeof(UpdateBlogOwnerTestData))]
public async Task WhenOwnerOfBlogUpdateBlogThenTheCorrespondingBlogIsUpdated(Blog blog, string userName)
{
var blogToUpdate = new Blog
{
BlogId = 1,
Url = "http://www.jean.dupont.com",
Description = "lorem",
Owner = "jean.dupont"
};
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().GetByIdAsync(It.IsAny<int>())).Returns(Task.FromResult(blogToUpdate)).Verifiable();
_unitOfWorkMock.Setup(r => r.GetRepository<Blog>().Update(blog)).Verifiable();
await _blogService.UpdateAsync(blog, userName);
_unitOfWorkMock.Verify(r => r.GetRepository<Blog>().GetByIdAsync(blog.BlogId));
_unitOfWorkMock.Verify(r => r.GetRepository<Blog>().Update(blogToUpdate), Times.Once,
"update must be called only once");
_unitOfWorkMock.Verify(r => r.SaveAsync(), Times.Once, "update must be called only once");
}
public class UpdateBlogOwnerTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[]
{
new Blog
{
BlogId = 1,
Url = "http://www.new1.jean.dupont.com",
Description = "new lorem 1",
Owner = "jean.dupont"
},
"jean.dupont"
};
yield return new object[] {
new Blog
{
BlogId = 2,
Url = "http://www.new2.jean.dupont.com",
Description = "new lorem 2",
Owner ="jean.dupont"
},
"jean.dupont"
};
yield return new object[] {
new Blog
{
BlogId = 3,
Url = "http://www.new3.jean.dupont.com",
Description = "new lorem 3",
Owner ="jean.dupont"
},
"jean.dupont"
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Test if SaveAsync is called
_unitOfWorkMock.Verify(r => r.SaveAsync(), Times.Once, "update must be called only once");
Regards



