Introduction
.NET Core is now the default standard for any web application development in the .NET world. However, with every application development, it is important to unit test your code.
Fortunately, .NET core has excellent support for MS Test. This article describes how you can use MS Test to write unit test cases for your API.
Why MS Test, Why Not xUnit?
xUnit is an excellent test framework but I found it lacks features which we are used to over the years while writing test cases with MS Test. One of the major limitations with xunit is that it does not do anything similar to AssemblyInitialize
, which is where we initialize anything before any of the test cases are executed.
I use AssemblyInitialize
to register my DI for other global initialization. If you are like me who likes to initialize test projects, MS Test is the default choice.
.NET core has excellent support for MS Test.
Using the Code
I am using Visual Studio 2017 for creating my API project and writing MS Test cases. Follow the below steps to write MS Test cases:
- Add a new project of type "Unit Test Project (.NET Core)" to your solution.
- Add reference to other projects in your solution.
- Add nuget package "
Microsoft.AspNetCore.TestHost
". Add "Moq
" nuget package if you would like to use mocking.
- Create a copy of Startup.cs from your main API project into your test project. I have renamed it to "TestStartup.cs" just so that I don't accidentally touch the main Startup.cs.
- Add a TestInitializer.cs (rename the class to whatever you like) in your test project.
- Add the logic to start test server and register the mock repositories.
[TestClass]
public class TestInitializer
{
public static HttpClient TestHttpClient;
public static Mock<IEmployeeRepository> MockEmployeeRepository;
[AssemblyInitialize]
public static void InitializeTestServer(TestContext testContext)
{
var testServer = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>()
.UseEnvironment("IntegrationTest"));
TestHttpClient = testServer.CreateClient();
}
public static void RegisterMockRepositories(IServiceCollection services)
{
MockEmployeeRepository = (new Mock<IEmployeeRepository>());
services.AddSingleton(MockEmployeeRepository.Object);
}
}
- From "
ConfigureServices
" of TestStartup.cs, call method to initialize your repositories.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
....
TestInitializer.RegisterMockRepositories(services);
....
}
- Write your first test case:
[TestClass]
public class TestEmployee
{
[TestMethod]
public void TestGetAllEmployees()
{
var mockEmps = new List<Employee>();
mockEmps.Add(new Employee
{ EmpId = 1, FirstName = "F1", LastName = "L1", Email = "F1.L1@tt.com" });
mockEmps.Add(new Employee
{ EmpId = 2, FirstName = "F2", LastName = "L2", Email = "F2.L2@tt.com" });
var employeeRepositoryMock = TestInitializer.MockEmployeeRepository;
employeeRepositoryMock.Setup
(x => x.GetEmployees()).Returns(Task.FromResult(mockEmps));
var response = TestInitializer.TestHttpClient.GetAsync("api/Employees").Result;
var resp = response.Content.ReadAsStringAsync().Result;
var responseData = JsonConvert.DeserializeObject<List<Employee>>(resp);
Assert.AreEqual(3, responseData.Count);
Assert.AreEqual(mockEmps[0].EmpId, responseData[0].EmpId);
}
}
- You can now debug/run your test case and view the results in Test Explorer.
Ponts of Interest
It is a good practice not to have any other code in controllers that make the call to your business logic layer. People tend to write test cases just for their business logic as they feel the controller doesn't have any code. I prefer writing test cases that call the controller as it traverses the full hierarchy.
You can download the sample code from below this github link.
One major limitiation is that Visual Studio doesn't support code coverage for .NET core projects, which is detailed at this link. It appears that Update 3 of Visual Studio 2017 does support code coverage but I didn't get a chance to download and play with Update 3. One option to get code coverage for .NET core projects is to use open source opencover.