How To Implement Of Unit Test Using Xunit And Moq n .NET Core 6 Web API

Introduction

In the early stages of software development, the smallest components are tested using a software design pattern called unit testing.

Prior to transferring to the production environment and QA Team, unit testing is done to validate the functionality that is to provide the intended output.
It facilitates the early detection of problems in the software development cycle.

When using the.NET Framework, there are numerous unit test tools available, including xUnit, NUnit, and many others.

xUnit

A unit testing framework for.NET development called xUnit is free and open-source.
Numerous capabilities of xUnit enable the creation of clear and effective unit test cases.
It gives a framework for creating our own characteristics and has several attributes, like Fact, Theory, and many more, to help test cases be written clearly and successfully.

Attributes of xUnit

In.NET, xUnit uses the [Fact] attribute to identify the unit test function.

[Fact]
public void EvenNumberTest() {
    //Arrange
    var num = 6;
    //Act
    bool result = Mathematics.IsEvenNumber(num);
    //Assert
    Assert.True(result);
}

The test method’s [theory] element is used to provide arguments.

[Theory]
[InlineData(5)]
public void OddNumberTest(int num) {
    //Act
    bool result = Mathematics.IsOddNumber(num);
    //Assert
    Assert.True(result);
}

Test Pattern

Writing unit test cases in the arrange-act-assert format makes them cleaner and easier to read.

Arrange

We set up and declare a few inputs and configuration variables in the arrange section.

Act

We included key components and functionality, such as method calls, API calls, and similar items, in the Act section.

Assert

Check the expected outputs to ensure they comply with our functional requirements.

Moq

  • In essence, Moq is the library that is utilized for mocking.
  • Assuming that our application depends on one or more services, we can utilize the Moq library to mock specific classes and functions with fictitious data instead of having to initialize everything linked to those services.

Step 1

Create a new .NET Core API Project

Step 2

Configure your project

Step 3

Provide additional information about your project

Step 4

Project Structure

Step 5

Install Following NuGet Packages

Step 6

Create the Models folder and create a new class Product

namespace UnitTestMoqFinal.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public string ProductDescription { get; set; }
        public int ProductPrice { get; set; }
        public int ProductStock { get; set; }
    }
}

Step 7

For data manipulation, create DbContextClass in the Data folder.

using Microsoft.EntityFrameworkCore;
using UnitTestMoqFinal.Models;

namespace UnitTestMoqFinal.Data
{
    public class DbContextClass : DbContext
    {
        protected readonly IConfiguration Configuration;

        public DbContextClass(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
        }
        public DbSet<Product> Products { get; set; }
    }
}

Step 8

Later, inside the Services folder, create the IProductService and ProductService classes for dependency injection and abstraction.

using UnitTestMoqFinal.Models;

namespace UnitTestMoqFinal.Services
{
    public interface IProductService
    {
        public IEnumerable<Product> GetProductList();
        public Product GetProductById(int id);
        public Product AddProduct(Product product);
        public Product UpdateProduct(Product product);
        public bool DeleteProduct(int Id);
    }
}

Create ProductService class

using UnitTestMoqFinal.Data;
using UnitTestMoqFinal.Models;

namespace UnitTestMoqFinal.Services
{
    public class ProductService : IProductService
    {
        private readonly DbContextClass _dbContext;

        public ProductService(DbContextClass dbContext)
        {
            _dbContext = dbContext;
        }
        public IEnumerable<Product> GetProductList()
        {
            return _dbContext.Products.ToList();
        }
        public Product GetProductById(int id)
        {
            return _dbContext.Products.Where(x => x.ProductId == id).FirstOrDefault();
        }

        public Product AddProduct(Product product)
        {
            var result = _dbContext.Products.Add(product);
            _dbContext.SaveChanges();
            return result.Entity;
        }

        public Product UpdateProduct(Product product)
        {
            var result = _dbContext.Products.Update(product);
            _dbContext.SaveChanges();
            return result.Entity;
        }
        public bool DeleteProduct(int Id)
        {
            var filteredData = _dbContext.Products.Where(x => x.ProductId == Id).FirstOrDefault();
            var result = _dbContext.Remove(filteredData);
            _dbContext.SaveChanges();
            return result != null ? true : false;
        }
    }
}

Step 9

After that, Create a new ProductController

using Microsoft.AspNetCore.Mvc;
using UnitTestMoqFinal.Models;
using UnitTestMoqFinal.Services;

namespace UnitTestMoqFinal.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly IProductService productService;
        public ProductController(IProductService _productService)
        {
            productService = _productService;
        }

        [HttpGet("productlist")]
        public IEnumerable<Product> ProductList()
        {
            var productList = productService.GetProductList();
            return productList;

        }
        [HttpGet("getproductbyid")]
        public Product GetProductById(int Id)
        {
            return productService.GetProductById(Id);
        }

        [HttpPost("addproduct")]
        public Product AddProduct(Product product)
        {
            return productService.AddProduct(product);
        }

        [HttpPut("updateproduct")]
        public Product UpdateProduct(Product product)
        {
            return productService.UpdateProduct(product);
        }

        [HttpDelete("deleteproduct")]
        public bool DeleteProduct(int Id)
        {
            return productService.DeleteProduct(Id);
        }
    }
}

Step 10

Add connection string inside app setting file

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=DESKTOP-Server;Initial Catalog=UnitTestMoqFinal;User Id=***;Password=***;"
  }
}

Step 11

Next, register a few services inside Program Class

using UnitTestMoqFinal.Data;
using UnitTestMoqFinal.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddDbContext<DbContextClass>();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Step 12

Execute the following entity framework command under the main project’s package manager console to add migrations and update the database.

“First” add-migration

update-database

The.NET Core Web API is the focus of this discussion. Let’s build a new Xunit project and use Moq to develop the test cases inside of it.

Step 13

Finally, run your application and you will see swagger UI and API endpoints

Create a new Xunit project

Step 1

Add a new Xunit project inside the existing solution

Step 2

Configure your new project

Step 3

Provide some additional information

Step 4

Install Moq NuGet Package for mocking purpose

Step 5

Create UnitTestController Class

using Moq;
using UnitTestMoqFinal.Controllers;
using UnitTestMoqFinal.Models;
using UnitTestMoqFinal.Services;

namespace UnitTestProject
{
    public class UnitTestController
    {
        private readonly Mock<IProductService> productService;
        public UnitTestController()
        {
            productService = new Mock<IProductService>();
        }

        [Fact]
        public void GetProductList_ProductList()
        {
            //arrange
            var productList = GetProductsData();
            productService.Setup(x => x.GetProductList())
                .Returns(productList);
            var productController = new ProductController(productService.Object);

            //act
            var productResult = productController.ProductList();

            //assert
            Assert.NotNull(productResult);
            Assert.Equal(GetProductsData().Count(), productResult.Count());
            Assert.Equal(GetProductsData().ToString(), productResult.ToString());
            Assert.True(productList.Equals(productResult));
        }

        [Fact]
        public void GetProductByID_Product()
        {
            //arrange
            var productList = GetProductsData();
            productService.Setup(x => x.GetProductById(2))
                .Returns(productList[1]);
            var productController = new ProductController(productService.Object);

            //act
            var productResult = productController.GetProductById(2);

            //assert
            Assert.NotNull(productResult);
            Assert.Equal(productList[1].ProductId, productResult.ProductId);
            Assert.True(productList[1].ProductId == productResult.ProductId);
        }

        [Theory]
        [InlineData("IPhone")]
        public void CheckProductExistOrNotByProductName_Product(string productName)
        {
            //arrange
            var productList = GetProductsData();
            productService.Setup(x => x.GetProductList())
                .Returns(productList);
            var productController = new ProductController(productService.Object);

            //act
            var productResult = productController.ProductList();
            var expectedProductName = productResult.ToList()[0].ProductName;

            //assert
            Assert.Equal(productName, expectedProductName);

        }


        [Fact]
        public void AddProduct_Product()
        {
            //arrange
            var productList = GetProductsData();
            productService.Setup(x => x.AddProduct(productList[2]))
                .Returns(productList[2]);
            var productController = new ProductController(productService.Object);

            //act
            var productResult = productController.AddProduct(productList[2]);

            //assert
            Assert.NotNull(productResult);
            Assert.Equal(productList[2].ProductId, productResult.ProductId);
            Assert.True(productList[2].ProductId == productResult.ProductId);
        }


        private List<Product> GetProductsData()
        {
            List<Product> productsData = new List<Product>
        {
            new Product
            {
                ProductId = 1,
                ProductName = "IPhone",
                ProductDescription = "IPhone 12",
                ProductPrice = 55000,
                ProductStock = 10
            },
             new Product
            {
                ProductId = 2,
                ProductName = "Laptop",
                ProductDescription = "HP Pavilion",
                ProductPrice = 100000,
                ProductStock = 20
            },
             new Product
            {
                ProductId = 3,
                ProductName = "TV",
                ProductDescription = "Samsung Smart TV",
                ProductPrice = 35000,
                ProductStock = 30
            },
        };
            return productsData;
        }
    }
}
  • You can see here that we added a reference to our main project before adding references to the current unit test project.
  • Inside the function Object() { [native code] }, we create a dummy IProductService instance.
  • Then, we create a test case that requires a list of products.
  • We then take a list of items from our custom method, which is located at the bottom of the same class.
  • Next, create a dummy data set for the product service’s list of products.
  • Additionally, because our product controller depends on the product service, we send the product service object within the function Object() { [native code] } of the product controller to resolve some dependencies.
  • We invoke the ProductList method of the controller in the act section.
  • Finally, we use a few conditions in the assert section to compare the actual and anticipated results.

Similar to this, each test case is completed step-by-step.

Step 6

Next, launch the Test Explorer inside of Visual Studio’s Test Section to examine all the test cases that we’ve written for the UnitTestControllerClass.

Step 7

Final Project Structure

Step 8

In addition, if you want to debug a test case, simply right-click on the test case and select debug after attaching the debugger point inside the test case. Finally, run your test cases to see if they worked successfully or not.

Conclusion

Unit tests and various properties with patterns are now being used. Following then, the use of Moq was addressed. Additionally, the implementation of.NET Core 6 is step-by-step.

Submit a Comment

Your email address will not be published.

Subscribe

Select Categories