Skip to main content

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

End Manual Testing๐Ÿ™‹‍♂️, Master .NET 8 Unit Testing Repositories 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

# Install Packages to the Api

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="x.x.x" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="x.x.x" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="x.x.x"/>


# Create Model

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


  # Create Application Db Context File

   public class UserDbContext : DbContext
   {
     public UserDbContext(DbContextOptions<UserDbContext> options) : base(options)
     {
     }
     public DbSet<User> Users { get; set; }
   }

       

# Create your Repository in API

 public interface IUserInterface
     {
         // 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 UserRepository(UserDbContext context) : IUserInterface
{
    public async Task<bool> CreateAsync(User user)
    {
        context!.Users.Add(user);
        var result = await context.SaveChangesAsync();
        return result > 0;
    }

    public async Task<bool> DeleteAsync(int id)
    {
        var getUser = await context.Users.FirstOrDefaultAsync(_ => _.Id == id);
        if (getUser != null)
        {
            context.Users.Remove(getUser);
            var result = await context.SaveChangesAsync();
            return result > 0;
        }
        return false;
    }

    public async Task<IEnumerable<User>> GetAllAsync() => await context!.Users.ToListAsync();

    public async Task<User> GetByIdAsync(int id)
    => await context!.Users!.FirstOrDefaultAsync(_ => _.Id == id);

    public async Task<bool> UpdateAsync(User user)
    {
        var getUser = await context.Users.FirstOrDefaultAsync(_ => _.Id == user.Id);
        if (getUser != null)
        {
            getUser.Name = user.Name;
            getUser.Email = user.Email;
            var result = await context.SaveChangesAsync();
            return result > 0;
        }
        return false;
    }
}


# Register in Program.cs file

builder.Services.AddDbContext<UserDbContext>
    (o => o.UseSqlite("Data Source = UnitTestDb.db"));
builder.Services.AddScoped<IUserInterface, UserRepository>();


# Add Database Migration

add-migration First
update-database


  # Create Controller

  [ApiController]
[Route("[controller]")]
public class UserController(IUserInterface userInterface) : ControllerBase
{
    // Create
    [HttpPost("add")]
    public async Task<IActionResult> Create(User user)
    {
        var result = await userInterface.CreateAsync(user);
        if (result)
            return CreatedAtAction(nameof(Create), new { id = user.Id }, user);
        else
            return BadRequest();
    }


    // Read All
    [HttpGet("get")]
    public async Task<IActionResult> GetUsers()
    {
        var users = await userInterface.GetAllAsync();
        if (!users.Any())
            return NotFound();
        else
            return Ok(users);
    }

    // Read single
    [HttpGet("get-single/{id:int}")]
    public async Task<IActionResult> GetUser(int id)
    {
        var user = await userInterface.GetByIdAsync(id);
        if (user is null)
            return NotFound();
        else
            return Ok(user);
    }
    // Update
    [HttpPut("update")]
    public async Task<IActionResult> UpdateUser(User user)
    {
        var result = await userInterface.UpdateAsync(user);
        if (result)
            return Ok();
        else
            return NotFound();
    }

    // Delete
    [HttpDelete("delete/{id}")]
    public async Task<IActionResult> DeleteUser(int id)
    {
        var result = await userInterface.DeleteAsync(id);
        if (result)
            return NoContent();
        else
            return NotFound();
    }
}


# Install these Packages to the XUnit project

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

    

# Now Create Unit Test To The Repository

namespace Test.Repository
{
    public class UserRepositoryTest
    {
        private readonly UserRepository UserRepository;
        public UserRepositoryTest()
        {
            var userDbContext = GetDatabaseContext().Result;
            UserRepository = new UserRepository(userDbContext);
        }
        private async Task<UserDbContext> GetDatabaseContext()
        {
            var options = new DbContextOptionsBuilder<UserDbContext>()
                .UseSqlite("Data Source = UsersDb.db").Options;
            var userDbContext = new UserDbContext(options);
            userDbContext.Database.EnsureCreated();

            if (!await userDbContext.Users.AnyAsync())
            {
                userDbContext.Users.Add(new User()
                { Email = "Email@mail.com", Name = "TUserRepo" });
                await userDbContext.SaveChangesAsync();
            }
            return userDbContext;
        }


        // Create
        // This return bool (true) for success
        [Fact]
        public async void UserRepository_Create_ReturnTrue()
        {
            // Arrange
            var user = A.Fake<User>();

            // Act
            var result = await UserRepository.CreateAsync(user);

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

        // Read All
        // This return List of User
        [Fact]
        public async void UserRepository_GetUsers_ReturnUsers()
        {
            // Arrange

            // Act
            var result = await UserRepository.GetAllAsync();

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

        // Read Single
        // This returns User object
        [Theory]
        [InlineData(2)]
        public async void UserRepository_GetUser_ReturnUser(int id)
        {
            //Arrange

            //Act
            var result = await UserRepository.GetByIdAsync(id);

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

        // Update
        // This returns true (success)
        [Theory]
        [InlineData(2)]
        public async void UserRepository_UpdateUser_ReturnTrue(int id)
        {
            // Arrange

            // Act
            var user = await UserRepository.GetByIdAsync(id);
            user.Name = "Updated";
            var result = await UserRepository.UpdateAsync(user);

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

        // Delete
        // This returns True
        [Fact]
        public async void UserRepository_DeleteUser_ReturnTrue()
        {
            // Arrange
            int id = 3;

            // Act
            var result = await UserRepository.DeleteAsync(id);

            // 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.


   # Output





# 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