Introduction
There are three different test frameworks that are supported by ASP.NET Core for unit testing - MSTest, xUnit, and NUnit. These allow us to test our code in a consistent way. In this article, I will explain about the unit test using NUnit.
NUnit is an open-source unit test framework for all .NET languages. It is initially ported from JUnit. The current released version of NUnit is 3, which has been completely rewritten with many new features.
To demonstrate the example of the unit test, I created an MVC project, a solution, and a unit test project by using CLI (Command Line Interface). To create an MVC and Test project, I am following the below steps.
Step 1. Create a solution file using the following command. This command creates an empty solution.
dotnet new sln -n MVCUnittest
Step 2. Create an MVC project using the following command.
dotnet new MVC
Step 3. To add this project to the solution, just use the following command.
dotnet sln add Unittest\Unittest.csproj
Step 4. Create an NUnit test project
By default, there is no direct template available for NUnit. If we want a template for NUnit, we need to install it using the following command.
dotnet new -i NUnit3.DotNetNew.Template
As an alternate way, we can create a class library project and add a reference to NUnit. The current version of NUnit is 3. It can be installed either from NuGet packages or from the .NET Core CLI tool. The following command is used to install NUnit using NuGet packages (Package Manager).
PM> Install-Package NUnit -Version 3.9.0
Using the following .NET CLI command, we can install NUnit.
dotnet add package NUnit --version 3.9.0
If the template for NUnit has been installed, we can create a NUnit project using the following command.
dotnet new nunit
This command creates a NUnit Test Project and generates a template that configures the Test runner into a .csproj file.
<ItemGroup>
<PackageReference Include="nunit" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
</ItemGroup>
The generated code also has dummy unit test files. It looks as follows.
using NUnit.Framework;
namespace Tests
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
Assert.Pass();
}
}
}
NUnit has a very similar attribute terminology as MSTest. The TextFixture is the same as the TestClass attribute in MSTest; it denotes a class that contains the unit test. The Test attribute is the same as the TestMethod attribute in MSTest; it denotes that a method is a test method. The SetUp attribute is used to identify a method that is called immediately before each test.
Step 5. Adding test project to the solution.
dotnet sln add TestProject\Testproject.csproj
To demonstrate the concept, I have created a method within the HomeController class (GetEmployeeName). This method accepts embed as a parameter, and based on that, it will return the name of the employee or "Not Found" hard code string.
HomeController
public string GetEmployeeName(int empId)
{
string name;
if (empId == 1)
{
name = "Jignesh";
}
else if (empId == 2)
{
name = "Rakesh";
}
else
{
name = "Not Found";
}
return name;
}
In the following test method, I have pass the hardcoded value and checked the result using the Assert class.
UnitTest1.cs
using NUnit.Framework;
using UnitTest.Controllers;
namespace Tests
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
HomeController home = new HomeController();
string result = home.GetEmployeeName(1);
Assert.AreEqual("Jignesh", result);
}
}
}
The final step is to run the Unit test. Using the following command, we can run all our test cases.
dotnet test
dotnet test --filter "FullyQualifiedName=TestProject.UnitTest1.Test1"
Result
We also run all test cases or individual tests within Visual Studio using Test Explore.
In the preceding example, my test result (actual) matched with the expected result. In the following example, my actual result does not match with the expected result.
[Test]
public void Test2()
{
HomeController home = new HomeController();
string result = home.GetEmployeeName(1);
Assert.AreEqual("Rakesh", result);
}
Result
To unit test every block of code, we require more test data. We can add more test methods using the Test attribute, but it is a very tedious job.
NUnit also supports other attributes which enable us to write a suite for similar tests. A TestCases attribute can be applied to the test and can take the test data directly using the provided data or Excel spreadsheet. Instead of creating a new test, we can use this attribute to create a single data-driven test.
The TestCase attribute may appear one or more times on a test method that contains the test data as a parameter. The method may optionally be marked with a Test attribute as well.
using NUnit.Framework;
using UnitTest.Controllers;
namespace Tests
{
public class Tests
{
[TestCase(1, "Jignesh")]
[TestCase(2, "Rakesh")]
[TestCase(3, "Not Found")]
public void Test3(int empId, string name)
{
HomeController home = new HomeController();
string result = home.GetEmployeeName(empId);
Assert.AreEqual(name, result);
}
}
}
Unit test with ILogger
.NET Core supports built-in dependency injection. So, whatever services we want to use during the execution of the code are injected as dependencies. One of the best examples is the ILogger Service. Using the following code, we can configure the ILogger service in our ASP.NET Core project.
Configure ILogger in the Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace Unittest
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.UseStartup<Startup>()
.Build();
}
}
TestController
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace Unittest.Controllers
{
public class TestController : Controller
{
private readonly ILogger _logger;
public TestController(ILogger<TestController> logger)
{
_logger = logger;
}
public string GetMessage()
{
_logger.LogDebug("Test Method Called!!!");
return "Hi! Reader";
}
}
}
Unit Test Method
To unit test the controller dependent on the ILogger service, we have to pass the ILogger object or null value to the constructor. To create these types of dependencies, we can create an object of service provider, and with the help of the service provider, we can create the object of such services.
In the following code, I created a service provider object and created an ILogger object.
[Test]
public void Test4()
{
var serviceProvider = new ServiceCollection()
.AddLogging()
.BuildServiceProvider();
var factory = serviceProvider.GetService<ILoggerFactory>();
var logger = factory.CreateLogger<TestController>();
TestController home = new TestController(logger);
string result = home.GetMessage();
Assert.AreEqual("Hi! Reader", result);
}
Summary
A unit test is a code that helps us verify 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). All Unit test frameworks - MSTest, XUnit, and NUnit - offer a similar end goal and help us to write unit tests that are simpler, easier, and faster. It is possible to use NUnit with .NET Core, but some tasks need to be done manually because there is no template available with .NET Core.