Unit Test In .NET Core Application Using MSTest

Introduction
 
There are three different test frameworks which are supported by the unit test with asp.net core: MSTest, xUnit, and NUnit, which allow us to test our code in a consistent way. In this article, I will explain about the unit test in asp.net core using MSTest.
 
To demonstrate the example of the unit test, I have created an MVC project, solution and unit test project by using CLI (Command Line Interface). To create the MVC and Test project, I am following the steps given below.
 
Create Solution file using the following command. This command creates the empty solution. 
  1. dotnet new sln -n MVCUnittest   
Create MVC Project: Using the following command, MVC project will be created
  1. >dotnet new MVC  
Adding this project to solution: Using the following command we can add project to solution
  1. >dotnet sln add Unittest\Unittest.csproj  
Create MsTest Project: Using the following command, we can create an MSTest project.
  1. >dotnet new mstest  
This command creates MSTest Project and the generated template configures test runner into .csproj file
  1. <ItemGroup>  
  2.   <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />  
  3.   <PackageReference Include="MSTest.TestAdapter" Version="1.1.18" />  
  4.   <PackageReference Include="MSTest.TestFramework" Version="1.1.18" />  
  5. </ItemGroup>  
The generated code also has a dummy unit test file. It looks as follows.
  1. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  2.   
  3. namespace Testproject  
  4. {  
  5.     [TestClass]  
  6.     public class UnitTest1  
  7.     {  
  8.         [TestMethod]  
  9.         public void TestMethod1()  
  10.         {  
  11.               
  12.         }  
  13.     }  
  14. }  
The TestClass attribute denotes the class which contains unit tests and TestMethod attribute denotes that a method is a test method.

Adding test project to solution
  1. >dotnet sln add TestProject\Testproject.csproj  
To demonstrate the concept, I have created a method within HomeController class (GetEmployeeName). This method accepts empId as a parameter and based on this, it will return the name of the employee or "Not Found" hard code string.
 
HomeController
  1. public string GetEmployeeName(int empId)  
  2. {  
  3.     string name;  
  4.     if (empId == 1)  
  5.     {  
  6.         name = "Jignesh";  
  7.     }  
  8.     else if (empId == 2)  
  9.     {  
  10.         name = "Rakesh";  
  11.     }  
  12.     else  
  13.     {  
  14.         name = "Not Found";  
  15.     }  
  16.     return name;  
  17. }  
In the following test method, I have passed a hardcoded value and checked the result using Assert class.
 
Unittest1.cs
  1. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  2. using Unittest.Controllers;  
  3.   
  4. namespace TestProject1  
  5. {  
  6.     [TestClass]  
  7.     public class UnitTest1  
  8.     {  
  9.         [TestMethod]  
  10.         public void TestMethod1()  
  11.         {  
  12.             HomeController home = new HomeController();  
  13.             string result = home.GetEmployeeName(1);  
  14.             Assert.AreEqual(result, "Jignesh");  
  15.         }  
  16.     }  
  17. }  
The final step is to run the Unit test. Using the following command, we can run all our test cases.
  1. >dotnet test  
Result

 
We also run all test cases or individual tests within Visual Studio using Test Explore.
 
 
In the preceding example, my test result (actual) is matched with the expected result. In the following example, my actual result is not matched with the expected result.
  1. [TestMethod]  
  2. public void TestMethod2()  
  3. {  
  4.     HomeController home = new HomeController();  
  5.     string result = home.GetEmployeeName(2);  
  6.     Assert.AreEqual(result, "Jignesh");  
  7. }  
Result

 

To unit test every block of code, we require more test data. We can add more test methods using TestMethod attribute, but it is a very tedious job. The MSTest project is also supported with another attribute which enables us to write a suite for a similar test. DataTestMethod attributes represent a suite of tests which executes the same code with different input arguments. A DataRow attribute can be used for specifying the values for those inputs. Instead of creating a new test, we can use these two attributes: DataTestMethod and DataRow to create a single data-driven test.
  1. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  2. using Unittest.Controllers;  
  3.   
  4. namespace TestProject1  
  5. {  
  6.     [TestClass]  
  7.     public class UnitTest2  
  8.     {  
  9.         [DataTestMethod]  
  10.         [DataRow(1, "Jignesh")]  
  11.         [DataRow(2, "Rakesh")]  
  12.         [DataRow(3, "Not Found")]  
  13.         public void TestMethod1(int empId, string name)  
  14.         {  
  15.             HomeController home = new HomeController();  
  16.             string result = home.GetEmployeeName(empId);  
  17.             Assert.AreEqual(result, name);  
  18.         }  
  19.     }  
  20. }  
Result

 

Unit test with ILogger
 
The .net core support built-in dependency injection. So, whatever services we want to use during the execution of the code are injected as a dependency. One of the best examples is ILogger service. Using the following code, we can configure ILogger service in our asp.net core project.
 
Configure ILogger in Program.cs
  1. using Microsoft.AspNetCore;  
  2. using Microsoft.AspNetCore.Hosting;  
  3. using Microsoft.Extensions.Logging;  
  4.   
  5. namespace Unittest  
  6. {  
  7.     public class Program  
  8.     {  
  9.         public static void Main(string[] args)  
  10.         {  
  11.             BuildWebHost(args).Run();  
  12.         }  
  13.   
  14.         public static IWebHost BuildWebHost(string[] args) =>  
  15.             WebHost.CreateDefaultBuilder(args)  
  16.                 .ConfigureLogging((hostingContext, logging) =>  
  17.                 {  
  18.                     logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));  
  19.                     logging.AddConsole();  
  20.                     logging.AddDebug();  
  21.                 })  
  22.                 .UseStartup<Startup>()  
  23.                 .Build();  
  24.     }  
  25. }  
TestController.cs
  1. using Microsoft.AspNetCore.Mvc;  
  2. using Microsoft.Extensions.Logging;  
  3.   
  4. namespace Unittest.Controllers  
  5. {  
  6.     public class TestController : Controller  
  7.     {  
  8.         private readonly ILogger _logger;  
  9.         public TestController(ILogger<TestController> logger)  
  10.         {  
  11.             _logger = logger;  
  12.         }  
  13.           
  14.         public string GetMessage()  
  15.         {  
  16.            _logger.LogDebug("Index Method Called!!!");  
  17.             return "Hi! Reader";  
  18.         }  
  19.     }  
  20. }  
Unit Test Method
 
For unit test controller which have the dependency on ILogger service, we have to pass ILogger object or null value to the constructor. To create these type of dependencies, we can create an object of a service provider and help with the service provided, we can create the object of such services.
 
In the following code, I have created service provider object and created the ILogger object.
  1. [TestMethod]  
  2. public void TestMethod4()  
  3. {  
  4.     var serviceProvider = new ServiceCollection()  
  5.         .AddLogging()  
  6.         .BuildServiceProvider();  
  7.   
  8.     var factory = serviceProvider.GetService<ILoggerFactory>();  
  9.   
  10.     var logger = factory.CreateLogger<TestController>();  
  11.     TestController home = new TestController(logger);  
  12.     string result = home.GetMessage();  
  13.     Assert.AreEqual(result, "Hi! Reader");  
  14. }  
Summary
 
A unit test is a code that helps us in verifying the expected behavior of the other code in isolation. Here “In isolation" means there is no dependency between the tests. This is a better idea to test the application code before it goes for quality assurance (QA).