Unit Testing Using XUnit And MOQ In ASP.NET Core

Writing unit tests can be difficult, time-consuming, and slow when you can't isolate the classes you want to test from the rest of the system. In this course, Mocking in .NET Core Unit Tests with Moq: Getting Started, you'll learn how to create mocks and use them as dependencies to the classes you want to test. First, you'll discover how to configure mocked methods and properties to return specific values. Next, you'll cover how to perform behavior/interaction testing. Finally, you'll explore how to set up mocked exceptions and events. When you're finished with this course, you'll have the necessary knowledge to use Moq to unit test your classes in isolation by creating and using mock objects.
 
Download the Source code - Git Hub  
 

Setup the Project

 
Let's create a sample web API Project with basic crud operations using EF Core code first approach.
 
 
 
 
Since .Net 5.0 installed on my machine so that I am going with the latest template we can choose what version we are comfortable with.
 
  
Create the Model Folder and inside will configure the Model class and DbContext for the EntityFramework Core Code First approach setup.
 
 
Employee.cs
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel.DataAnnotations;  
  4. using System.Linq;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace UnitTest_Mock.Model  
  8. {  
  9.     public class Employee  
  10.     {  
  11.         [Key]  
  12.         public int Id { getset; }  
  13.         public string Name { getset; }  
  14.         public string Desgination { getset; }  
  15.     }  
  16. }  
 AppDbContext.cs
  1. using Microsoft.EntityFrameworkCore;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace UnitTest_Mock.Model  
  8. {  
  9.     public partial class AppDbContext : DbContext  
  10.     {  
  11.         public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)  
  12.         {  
  13.   
  14.         }  
  15.         public DbSet<Employee> Employees { getset; }  
  16.     }  
  17. }  
Let's set up the connection string to perform the code first operations.
 
appsettings.json
  1. {  
  2.   "Logging": {  
  3.     "LogLevel": {  
  4.       "Default""Information",  
  5.       "Microsoft""Warning",  
  6.       "Microsoft.Hosting.Lifetime""Information"  
  7.     }  
  8.   },  
  9.   "AllowedHosts""*",  
  10.   "ConnectionStrings": {  
  11.     "myconn""server=Your server name; database=UnitTest;Trusted_Connection=True;"  
  12.   }  
  13. }  
 Startup.cs
  1. using Microsoft.AspNetCore.Builder;  
  2. using Microsoft.AspNetCore.Hosting;  
  3. using Microsoft.AspNetCore.HttpsPolicy;  
  4. using Microsoft.AspNetCore.Mvc;  
  5. using Microsoft.EntityFrameworkCore;  
  6. using Microsoft.Extensions.Configuration;  
  7. using Microsoft.Extensions.DependencyInjection;  
  8. using Microsoft.Extensions.Hosting;  
  9. using Microsoft.Extensions.Logging;  
  10. using Microsoft.OpenApi.Models;  
  11. using System;  
  12. using System.Collections.Generic;  
  13. using System.Linq;  
  14. using System.Threading.Tasks;  
  15. using UnitTest_Mock.Model;  
  16. using UnitTest_Mock.Services;  
  17.   
  18. namespace UnitTest_Mock  
  19. {  
  20.     public class Startup  
  21.     {  
  22.         public Startup(IConfiguration configuration)  
  23.         {  
  24.             Configuration = configuration;  
  25.         }  
  26.   
  27.         public IConfiguration Configuration { get; }  
  28.   
  29.         // This method gets called by the runtime. Use this method to add services to the container.  
  30.         public void ConfigureServices(IServiceCollection services)  
  31.         {  
  32.   
  33.             services.AddControllers();  
  34.             services.AddSwaggerGen(c =>  
  35.             {  
  36.                 c.SwaggerDoc("v1"new OpenApiInfo { Title = "UnitTest_Mock", Version = "v1" });  
  37.             });  
  38.             #region Connection String  
  39.             services.AddDbContext<AppDbContext>(item => item.UseSqlServer(Configuration.GetConnectionString("myconn")));  
  40.             #endregion  
  41.             services.AddScoped<IEmployeeService, EmployeeService>();  
  42.         }  
  43.   
  44.         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
  45.         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
  46.         {  
  47.             if (env.IsDevelopment())  
  48.             {  
  49.                 app.UseDeveloperExceptionPage();  
  50.                 app.UseSwagger();  
  51.                 app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json""UnitTest_Mock v1"));  
  52.             }  
  53.   
  54.             app.UseHttpsRedirection();  
  55.   
  56.             app.UseRouting();  
  57.   
  58.             app.UseAuthorization();  
  59.   
  60.             app.UseEndpoints(endpoints =>  
  61.             {  
  62.                 endpoints.MapControllers();  
  63.             });  
  64.         }  
  65.     }  
  66. }  
Create the tables by using the below commands in the console.
 
Step 1
 
To create a migration script 
  1. PM> Add-Migration 'Initial'  
Step 2
 
To execute the script in SQL Db
  1. PM> update-database  
Create a Services folder where we perform our business logic for all the operations.
 
 
 
EmployeeService.cs
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Threading.Tasks;  
  5. using UnitTest_Mock.Model;  
  6. using Microsoft.EntityFrameworkCore;  
  7.   
  8. namespace UnitTest_Mock.Services  
  9. {  
  10.     public class EmployeeService : IEmployeeService  
  11.     {  
  12.         #region Property  
  13.         private readonly AppDbContext _appDbContext;  
  14.         #endregion  
  15.  
  16.         #region Constructor  
  17.         public EmployeeService(AppDbContext appDbContext)  
  18.         {  
  19.             _appDbContext = appDbContext;  
  20.         }  
  21.         #endregion  
  22.   
  23.         public async Task<string> GetEmployeebyId(int EmpID)  
  24.         {  
  25.             var name = await _appDbContext.Employees.Where(c=>c.Id == EmpID).Select(d=> d.Name).FirstOrDefaultAsync();  
  26.             return name;  
  27.         }  
  28.   
  29.         public async Task<Employee> GetEmployeeDetails(int EmpID)  
  30.         {  
  31.             var emp = await _appDbContext.Employees.FirstOrDefaultAsync(c => c.Id == EmpID);  
  32.             return emp;  
  33.         }  
  34.     }  
  35. }  
IEmployeeService.cs
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Threading.Tasks;  
  5. using UnitTest_Mock.Model;  
  6.   
  7. namespace UnitTest_Mock.Services  
  8. {  
  9.    public interface IEmployeeService  
  10.     {  
  11.         Task<string> GetEmployeebyId(int EmpID);  
  12.         Task<Employee> GetEmployeeDetails(int EmpID);  
  13.     }  
  14. }  
Define these services in Startup. cs file which I have already highlighted in the above-mentioned startup.cs file.
 
Create API methods for those services in the controller class.
 
EmployeeController.cs
  1. using Microsoft.AspNetCore.Mvc;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Threading.Tasks;  
  6. using UnitTest_Mock.Model;  
  7. using UnitTest_Mock.Services;  
  8.   
  9. namespace UnitTest_Mock.Controllers  
  10. {  
  11.     [Route("api/[controller]")]  
  12.     [ApiController]  
  13.     public class EmployeeController : ControllerBase  
  14.     {  
  15.         #region Property  
  16.         private readonly IEmployeeService _employeeService;  
  17.         #endregion  
  18.  
  19.         #region Constructor  
  20.         public EmployeeController(IEmployeeService employeeService)  
  21.         {  
  22.             _employeeService = employeeService;  
  23.         }  
  24.         #endregion  
  25.   
  26.         [HttpGet(nameof(GetEmployeeById))]  
  27.         public async Task<string> GetEmployeeById(int EmpID)  
  28.         {  
  29.             var result = await _employeeService.GetEmployeebyId(EmpID);  
  30.             return result;  
  31.         }  
  32.         [HttpGet(nameof(GetEmployeeDetails))]  
  33.         public async Task<Employee> GetEmployeeDetails(int EmpID)  
  34.         {  
  35.             var result = await _employeeService.GetEmployeeDetails(EmpID);  
  36.             return result;  
  37.         }  
  38.   
  39.     }  
  40. }   
Let us create another testing project inside this solution project where we can write test cases for those functions
  • Right-click on the Solution
  • Click on Add - New project
  • Search for X-Unit Test project.
 
 
 
Choose the target framework same as where we have used in our API project.
 
 
 
Install the Moq package inside this unit test project.
 
 
 
Create a class inside this Test project to define all our respective test cases but before that, we have to insert data into the table which we have created. Open the SQL Server and insert dummy data to the employee table.
 
EmployeeTest.cs
  1. using Moq;  
  2. using UnitTest_Mock.Controllers;  
  3. using UnitTest_Mock.Model;  
  4. using UnitTest_Mock.Services;  
  5. using Xunit;  
  6.   
  7. namespace UnitTesting  
  8. {  
  9.    public class EmployeeTest  
  10.     {  
  11.         #region Property  
  12.         public Mock<IEmployeeService> mock = new Mock<IEmployeeService>();  
  13.         #endregion  
  14.   
  15.         [Fact]  
  16.         public async void GetEmployeebyId()  
  17.         {  
  18.             mock.Setup(p => p.GetEmployeebyId(1)).ReturnsAsync("JK");  
  19.             EmployeeController emp = new EmployeeController(mock.Object);  
  20.             string result = await emp.GetEmployeeById(1);  
  21.             Assert.Equal("JK", result);  
  22.         }  
  23.         [Fact]  
  24.         public async void GetEmployeeDetails()  
  25.         {  
  26.             var employeeDTO = new Employee()  
  27.             {  
  28.                 Id = 1,  
  29.                 Name = "JK",  
  30.                 Desgination = "SDE"  
  31.             };  
  32.             mock.Setup(p => p.GetEmployeeDetails(1)).ReturnsAsync(employeeDTO);  
  33.             EmployeeController emp = new EmployeeController(mock.Object);  
  34.             var result = await emp.GetEmployeeDetails(1);  
  35.             Assert.True(employeeDTO.Equals(result));  
  36.         }  
  37.     }  
  38. }  
setting up the mock for our API business services under the controller level to check the result and compare with user-defined values.
 
we can debug the test cases to check the output in running mode.
 
Run all the test cases to verify whether they are passed or failed.
  • Click on View in the Top left
  • Click on Test explorer. 
 
In the above image, we can see all our test cases are passed and their time duration as well
 
Hope this article helps you in understanding unit testing using the Mock object.
 
Keep learning ......!