Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Dec 25, 2023

ASP.NET Core integration test using Moq Framework

ASP.NET Core integration test using Moq Framework
Автор:
Источник:
Просмотров:
3098

Testing coverage is the key part of any project development and Testing coverage is not only about the unit testing instead Integration Testing is extremely important to make sure entire system is unbroken & bug free with any change or subsequent enhancement.

When we talk about integration testing, mocking dependencies are the oxygen to cover the integration testing. You may create a replica of your services as mock to work with integration testing but do we really need to do that, when Moq framework is doing the same for us and we can save lot of time & effort from writing unnecessary codes.

So lets see How to achieve Integration Testing for ASP.NET Core using Moq framework. My intention here is to only show the integration testing using Moq hence I’m doing a simple ASP.NET Core Web API project which has a service endpoint to return the weather forecast and goal is to test the end-to-end flow of api call by mocking the service which calculates the forecast.

I’m using .Net core 6, xUnit for testing and Moq for mocking.

Now, first we will be creating a blank solution named ‘MoqDemoSol.sln” and add a .net core 6 class library project named CoreWebAPIMoqDemo.Services and two files as:
IWeatherForecastService.cs: interface for business service to calculate weather forecast:

namespace CoreWebAPIMoqDemo.Services
{
    public interface IWeatherForecastService
    {
        Task<WeatherForecast[]> GetWeatherForecast();
    }

    public record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
    {
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

and

WeatherForecastService.cs: implementation of business service to calculate weather forecast:

namespace CoreWebAPIMoqDemo.Services
{
    public class WeatherForecastService : IWeatherForecastService
    {
        string[] summaries = new string[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
        
        public async Task<WeatherForecast[]> GetWeatherForecast()
        {
            var forecast = Enumerable.Range(1, 5).Select(index =>
                new WeatherForecast
                (
                    DateTime.Now.AddDays(index),
                    Random.Shared.Next(-20, 55),
                    summaries[Random.Shared.Next(summaries.Length)]
                )).ToArray();

            return forecast;
        }

    }
}

Our business layer service which calculates the weather forecast is ready.

Now let’s create a .net core 6 web api project named as ‘CoreWebAPIMoqDemo’ using the template and replace the code in Program.cs as:

using CoreWebAPIMoqDemo.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IWeatherForecastService, WeatherForecastService>();

var app = builder.Build();

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

app.UseHttpsRedirection();

app.MapGet("/weatherforecast", async (IWeatherForecastService service) =>
{
    return await service.GetWeatherForecast();
})
.WithName("GetWeatherForecast");


app.Run();

public partial class Program { }

Highlighted(in bold) codes are the modified code from the actual template Program.cs, Now if you run this you will see the service in swagger UI as:

Swagger

Now our application is ready, hence we will be moving forward to do the integration testing to test end-to-end flow of /weatherforecast service by mocking the service dependency of CoreWebAPIMoqDemo.Services.IWeatherForecastService.

Why we are doing this is, assume this is a third party service call and we can’t get this in our testing environment. Now lets add the unit test project (of your choice i.e. MSTest, nUnit, xUnit etc, in my case it is xUnit) named as ‘CoreWebAPIMoqDemo.Tests’ and install the package related to your unit test framework.

Next, since we will be using Moq framework for mocking hence install the Moq package through nuget package manager. In my case it is:

<PackageReference Include="Moq" Version="4.18.1" />

I’ll be keeping mocked service at once place hence adding a class named ‘MockServices.cs’ which will have the Mocked services as well as a method (which uses reflection) to return list of all mocked service’s interface type and mock object defined here. Code is:

using Moq;
using CoreWebAPIMoqDemo.Services;
using System.Reflection;

namespace CoreWebAPIMoqDemo.Tests
{
    internal class MockServices
    {
        public Mock<IWeatherForecastService> WeatherForecastServiceMock { get; init; }

        public MockServices()
        {
            WeatherForecastServiceMock = new Mock<IWeatherForecastService>();
        }

        /// <summary>
        /// This returns the collection of all mock service's interface type and the mock object, defined here i.e. <see cref="WeatherForecastServiceMock"/>.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<(Type, object)> GetMocks()
        {
            return GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Select(x =>
                {
                    var interfaceType = x.PropertyType.GetGenericArguments()[0];
                    var value = x.GetValue(this) as Mock;return (interfaceType, value.Object);
                })
                .ToArray();
        }
    }
}

The purpose of doing this is, to keep all mocked services at one place and then GetMocks() which help me get all the mocked object and interface type so that I’ll be able to inject this with .net core dependency container collection IServiceCollection to use during unit test execution.

Next adding a class named ‘TestMoqPOCApplication.cs’ which inherits WebApplicationFactory<Program> to override CreateHost() method to prepare the IServiceCollection for integration tests. code is:

using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using CoreWebAPIMoqDemo.Services;

namespace CoreWebAPIMoqDemo.Tests
{
    internal class TestMoqPOCApplication : WebApplicationFactory<Program>
    {
        private readonly MockServices _mockServices;
        public TestMoqPOCApplication(MockServices mockServices)
        {
            _mockServices = mockServices;
        }

        protected override IHost CreateHost(IHostBuilder builder)
        {
            builder.ConfigureServices(services => {
                foreach ((var interfaceType, var serviceMock) in _mockServices.GetMocks())
                {
                    services.Remove(services.SingleOrDefault(d => d.ServiceType == interfaceType));
                    services.AddSingleton(typeof(IWeatherForecastService), serviceMock);
                }
            });

            return base.CreateHost(builder);            
        }
    }
}

In above highlighted code, our MockServices instance is available through constructor and then inside CreateHost method looping through each mock services we defined in the MockService class and putting into dependency container by removing the existing one which will be having actual business implementation of IWeatherForecastService.

By this we are ready with mocking and now add the code for the integration test for /weatherforecast api call. Add a class WeatherForecastTests.cs and add the code into it as:

using Moq;
using CoreWebAPIMoqDemo.Services;
using Newtonsoft.Json;

namespace CoreWebAPIMoqDemo.Tests
{
    public class WeatherForecastTests
    {
        private readonly TestMoqPOCApplication _testMoqPOCApplication;
        private readonly MockServices _mockServices;
        public readonly HttpClient _client;
        
        public WeatherForecastTests()
        {
            _mockServices = new MockServices();

            // instantiating TestMoqPOCApplication
            _testMoqPOCApplication = new TestMoqPOCApplication(_mockServices);

            // creating client for api call
            _client = _testMoqPOCApplication.CreateClient();
        }

        [Fact]
        public async void GetWeatherForecastTest()
        {
            // mocking the business service's GetWeatherForecast() method to return result as below  
            var expResult = new WeatherForecast[]
            {
                new WeatherForecast(DateTime.Now, 26, "Bengaluru")
            };

            // mocking the business service's GetWeatherForecast()
            _mockServices.WeatherForecastServiceMock.Setup(m => m.GetWeatherForecast()).ReturnsAsync(expResult);

            //calling the api
            var response = await _client.GetAsync("/weatherforecast");
            string jsonString = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<WeatherForecast[]>(jsonString);

            //testing the response
            Assert.Equal(response.StatusCode, System.Net.HttpStatusCode.OK);
            Assert.Equal(expResult, result);}
    }
}

Whoop, we are done. now if you run the test, you will see the result as:

Test Explorer

Entire code is available here: https://github.com/binodmahto/FunProjects/tree/main/CoreWebAPIMoqDemo

That’s all. Hope you enjoyed the content, don’t forget to follow me for more like this and clap for it please. Happy programming.

Похожее
Jun 7, 2021
Author: Himanshu Sheth
One of the most challenging things to do is ‘making the right choice.’ Arriving at a decision becomes even more complicated when there are multiple options in front of you☺. The same is the case with choosing a testing framework...
Feb 17, 2023
Author: Juldhais Hengkyawan
A Guide to Building Scalable, Maintainable Web API using ASP .NET Core The term “Clean Architecture” has become increasingly popular in software development in recent years. Clean Architecture is a software design pattern that prioritizes the separation of concerns, making...
Aug 12
Author: Crafting-Code
Containerizing and deploying ASP.NET Core applications Containerization has transformed the way applications are developed, deployed, and managed in the modern software industry. Docker, in particular, has become a pivotal tool, simplifying the packaging and deployment of applications, including ASP.NET Core...
Jul 29
Author: Rick Strahl
Over the last few years, Markdown has become a ubiquitous text-entry model for HTML text. It's creeping up in more and more places and has become a standard for documents that are shared and edited for documentation purposes on the...
Написать сообщение
Тип
Почта
Имя
*Сообщение
RSS
Если вам понравился этот сайт и вы хотите меня поддержать, вы можете
Soft skills: 18 самых важных навыков, которыми должен владеть каждый работник
NULL в SQL: что это такое и почему его знание необходимо каждому разработчику
5 правил работы с суммами
Семь итераций наивности или как я полтора года свою дебютную игру писал
Почему вы никогда не должны соглашаться на собеседования с программированием
Какого черта мы нанимаем, или осмысленность собеседований в IT
Дюжина логических задач с собеседований
14 вопросов об индексах в SQL Server, которые вы стеснялись задать
Как мы столкнулись с версионированием и осознали, что вариант «просто проставить цифры» не работает
Функции и хранимые процедуры в PostgreSQL: зачем нужны и как применять в реальных примерах
LinkedIn: Sergey Drozdov
Boosty
Donate to support the project
GitHub account
GitHub profile