Skip to main content

๐Ÿš€Master User Authentication in .NET 8 Web API Email Confirmation, Password Reset, 2FA & Lockout with JWT & Identity๐Ÿ”

Transform Your Testing!๐Ÿ™‹‍♂️ Master .NET 8 Unit Testing HTTP Services with XUnit in Web API!

We're diving into the world of unit testing repositories in Web API using the XUnit framework. But wait, that's not all—we'll be spicing things up with the power of FakeItEasy and Fluent Assertions! If you’ve been looking to elevate your testing game, this video is for you. ๐Ÿš€

# Introduction

Imagine this scenario: you have a repository that manages data access for your application. You need to ensure it performs correctly without hitting the actual database. This is where unit testing comes into play, providing you the confidence to know your code works as expected. Ready to make your tests more reliable and readable? Let’s get started!

# What is Unit Testing?

Unit Testing is a software testing technique where individual components or units of a software are tested in isolation from the rest of the application. The primary goal of unit testing is to validate that each unit of the software performs as expected. This helps in identifying bugs early in the development cycle, making the software more robust and reducing the cost and effort required for later stages of testing.


# Benefits of Unit Testing:

1. Early Bug Detection: Identifies bugs early in the development process.

2. Simplifies Integration: Ensures that each unit works correctly before integrating them into the larger system.

3. Documentation: Serves as documentation for the code, explaining how each unit is expected to behave.

4. Refactoring Confidence: Gives developers confidence to refactor code without fear of introducing new bugs.


# What is Mocking?

Mocking is a technique used in unit testing to simulate the behavior of complex objects or external systems that a unit interacts with. By using mock objects, you can control the behavior of these dependencies, making it easier to test the unit in isolation.


# Benefits of Mocking:

1. Isolation: Tests the unit without relying on external systems, ensuring tests are fast and reliable.

2. Control: Allows you to simulate different scenarios and edge cases.

3. Focus: Ensures that tests focus on the behavior of the unit under test, rather than its dependencies.


 # Unit Testing Patterns  

1. System Under Test (SUT)

   The System Under Test (SUT) refers to the specific component or unit that is being tested. In unit testing, the SUT is isolated from its dependencies to ensure that tests are focused on the behavior of the SUT itself.

2. Arrange, Act, Assert (AAA) Pattern

   The Arrange, Act, Assert (AAA) pattern is a common structure for writing unit tests. It helps in organizing the test code in a clear and consistent manner.

   a. Arrange: Set up the conditions for the test. This includes creating objects, initializing variables, and configuring mock dependencies.

   b. Act: Perform the action that you want to test. This involves calling the method or function on the SUT.

   c. Assert: Verify the outcome of the action. This involves checking that the expected result matches the actual result.


# Create Web API project with XUnit Project

# Create Model

 public class User
 {
     public int Id { get; set; }
     public string? Name { get; set; }
     public string? Email { get; set; }
 }

# Install these Packages to the XUnit project

<PackageReference Include="FakeItEasy" Version="x.x.x" />
<PackageReference Include="FluentAssertions" Version="x.x.x" />

# Introduction

This is a crucial aspect of ensuring your applications communicate reliably and securely with external APIs.


# Why is it Important to Test HttpClient Service?

Testing HttpClient services is essential because it helps verify the correctness of your application's communication with external APIs. Here are a few reasons why it’s important:

1. Reliability: Ensure that your HttpClient service handles various responses correctly, including success, failure, and edge cases.

2. Security: Validate that your service properly handles sensitive information and errors without exposing vulnerabilities.

3. Performance: Check that your service performs efficiently, especially under load or when handling large amounts of data.

4. Maintainability: By having comprehensive tests, you can confidently make changes to your HttpClient service, knowing that any regressions will be caught early.


# Create Service Interface and Implementation class    

public interface IUserService
{  // CRUD
    Task<bool> CreateAsync(User user);
    Task<User> GetByIdAsync(int id);
    Task<IEnumerable<User>> GetAllAsync();
    Task<bool> UpdateAsync(User user);
    Task<bool> DeleteAsync(int id);
}

 public class UserService : IUserService
{
    private readonly HttpClient httpClient;

    public UserService(HttpClient httpClient)
    {
        this.httpClient = httpClient;
        this.httpClient.BaseAddress = new Uri("https://localhost:7120/");
    }
    public async Task<bool> CreateAsync(User user)
    {
        var result = await httpClient.PostAsJsonAsync("/user/add", user);
        if (result.IsSuccessStatusCode) return true;
        else return false;
    }

    public async Task<bool> DeleteAsync(int id)
    {
        var result = await httpClient.DeleteAsync($"User/delete/{id}");
        if (result.IsSuccessStatusCode) return true;
        else return false;
    }

    public async Task<IEnumerable<User>> GetAllAsync()
    => await httpClient.GetFromJsonAsync<IEnumerable<User>>("user/get");

    public async Task<User> GetByIdAsync(int id)
    => await httpClient.GetFromJsonAsync<User>($"user/get/{id}");

    public async Task<bool> UpdateAsync(User user)
    {
        var result = await httpClient.PutAsJsonAsync("User/update", user);
        if (result.IsSuccessStatusCode)
            return true!;
        else return false;
    }
}


# Register HttpClient and Create DI between Service

builder.Services.AddHttpClient<IUserService, UserService>();


# Create Fake HttpMessage Handler Class

public abstract class FakeableHttpMessageHandler : HttpMessageHandler
{
    public abstract Task<HttpResponseMessage> FakeSendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken);

    // sealed so FakeItEasy won't intercept calls to this method
    protected sealed override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        => this.FakeSendAsync(request, cancellationToken);
}

 

 # Create Fake HttpClient

public static class CustomFakeHttpClient
{
    public static HttpClient FakeHttpClient<T>(T content)
    {
        var response = new HttpResponseMessage
        {
            Content = new StringContent(JsonSerializer.Serialize(content))
        };

        var handler = A.Fake<FakeableHttpMessageHandler>();
        A.CallTo(() => handler.FakeSendAsync(
            A<HttpRequestMessage>.Ignored, A<CancellationToken>.Ignored))
            .Returns(response);

        return new HttpClient(handler);
    }
    
}


# Create Test Class For This Service

public class UserServiceTest
{
    // Create
    [Fact]
    public async void UserService_CreateUser_ReturnTrue()
    {
        // Arrange
        var user = A.Fake<User>();

        //Act
        var client = CustomFakeHttpClient.FakeHttpClient<bool>(true);
        var userService = new UserService(client);
        var result = await userService.CreateAsync(user);
        client.Dispose();

        // Assert
        result.Should().BeTrue();
    }

    // Read all
    [Fact]
    public async void UserService_GetUsers_ReturnUsers()
    {
        // Arrange
        var users = A.Fake<List<User>>();
        users.Add(new User() { Email = "Email@mail.com", Name = "Netcode-Hub", Id = 1 });


        // Act
        var client = CustomFakeHttpClient.FakeHttpClient<List<User>>(users);
        var userService = new UserService(client);
        var result = await userService.GetAllAsync();
        client.Dispose();

        //// Assert
        result.Should().AllBeOfType<User>();
        result.Should().NotBeEmpty();
    }

    // Read Single
    [Theory]
    [InlineData(1)]
    public async void UserService_GetUser_ReturnUser(int id)
    {
        // Arrange
        var user = A.Fake<User>();
        user.Email = "Email@mail.com"; user.Name = "Netcode-Hub"; user.Id = 1;


        /// Act
         var client = CustomFakeHttpClient.FakeHttpClient<User>(user);
        var userService = new UserService(client);
        var result = await userService.GetByIdAsync(id);
        client.Dispose();

        //// Assert
        result.Should().BeOfType<User>();
        result.Should().NotBeNull();
    }

    // Update
    [Fact]
    public async void UserService_UpdateUser_ReturnTrue()
    {
        // Arrange
        var user = A.Fake<User>();
        user.Email = "Email@mail.com"; user.Name = "Netcode-Hub"; user.Id = 1;

        /// Act
         var client = CustomFakeHttpClient.FakeHttpClient<bool>(true);
        var userService = new UserService(client);
        var result = await userService.UpdateAsync(user);
        client.Dispose();

        //// Assert
        result.Should().BeTrue();
    }

    // Delete
    [Fact]
    public async void UserService_DeleteUser_ReturnTrue()
    {
        // Arrange
        int userId = 1;

        /// Act
        var client = CustomFakeHttpClient.FakeHttpClient<bool>(true);
        var userService = new UserService(client);
        var result = await userService.DeleteAsync(userId);
        client.Dispose();

        //// Assert
        result.Should().BeTrue();
    }
}


# Summary

I've walked you through the essentials of unit testing Web API controllers using XUnit, FakeItEasy, and Fluent Assertions. We started by setting up our environment and writing our first test, then moved on to more advanced topics like mocking dependencies and writing fluent assertions. By now, you should have a solid understanding of how to write clean, maintainable unit tests that ensure your Web API controllers are robust and reliable.


# Conclusion

And that’s a wrap, Netcode-Hub Channel fam! Today, we’ve seen how to effectively test repositories in a Web API using XUnit, FakeItEasy, and Fluent Assertions. By following this approach, you can ensure your data access code is robust and error-free, saving you countless hours debugging issues in production. ๐ŸŒŸ


# Here's a follow-up section to encourage engagement and support for Netcode-Hub:

๐ŸŒŸ Get in touch with Netcode-Hub! ๐Ÿ“ซ

1. GitHub: [Explore Repositories] ๐ŸŒ

2. Twitter: [Stay Updated] ๐Ÿฆ

3. Facebook: [Connect Here]๐Ÿ“˜

4. LinkedIn: [Professional Network]๐Ÿ”—

5. Email: [business.netcodehub@gmail.com] ๐Ÿ“ง

# ☕️ If you've found value in Netcode-Hub's work, consider supporting the channel with a coffee!

1. Buy Me a Coffee: [Support Netcode-Hub] ☕️

Comments

Popular Posts

Complete Employee Management System | .NET 8 Blazor Wasm & Web API - Perform CRUD, Print, PDF etc..

.NET 8 Clean Architecture with Blazor CRUD, JWT & Role Authorization using Identity & Refresh Token๐Ÿ”ฅ

Employee Management System | .NET 8 Blazor Wasm- Profile & real-time data retrieval. Update 1