Fundamentals 18 min read

A Comprehensive Guide to Unit Testing: Concepts, Best Practices, and Code Examples

This article explains the fundamentals of unit testing, compares it with integration testing and test‑driven development, describes what makes a good unit test, shows how to improve code testability, and provides concrete C# examples using XUnit, FluentAssertions, and Moq.

Architecture Digest
Architecture Digest
Architecture Digest
A Comprehensive Guide to Unit Testing: Concepts, Best Practices, and Code Examples

In modern development, especially with front‑end/back‑end separation, unit testing has become essential for backend teams to ensure code correctness; the article starts by defining unit testing, contrasting it with integration testing, and outlining its relationship with test‑driven development (TDD).

It then lists the key characteristics of unit tests—repeatability, speed, isolation, single‑concern focus—and explains how integration tests differ by involving real external dependencies and covering multiple concerns.

The piece introduces TDD, describing the cycle of writing failing tests before code, and highlights its benefits for understanding requirements and reducing defects.

Concrete C# examples are provided, including a set of XUnit tests for a UserService using FluentAssertions and Moq, as well as a simple in‑memory MockRepository<T> implementation and a mock initialization snippet:

// UserServiceTests.cs
namespace RepositoryAndEf.Domain.Tests
{
    public class UserServiceTests
    {
        private IRepository
_userRepository = new MockRepository
();

        [Fact]
        public void RegisterUser_ExpectedParameters_Success()
        {
            var userService = new UserService(_userRepository);
            var registeredUser = userService.Register(
                "[email protected]",
                "Jesse",
                "Jesse");

            var userFromRepository = _userRepository.GetById(registeredUser.Id);
            userFromRepository.Should().NotBe(null);
            userFromRepository.Email.Should().Be("[email protected]");
            userFromRepository.Name.Should().Be("Jesse");
            userFromRepository.Password.Should().Be("Jesse");
        }

        [Fact]
        public void RegisterUser_ExistedEmail_ThrowException()
        {
            var userService = new UserService(_userRepository);
            var registeredUser = userService.Register(
                "[email protected]",
                "Jesse",
                "Jesse");

            var userFromRepository = _userRepository.GetById(registeredUser.Id);
            userFromRepository.Should().NotBe(null);

            Action action = () => userService.Register(
                "[email protected]",
                "Jesse_01",
                "Jesse");
            action.ShouldThrow
();
        }

        public void RegisterUser_ExistedName_ThrowException()
        {
            var userService = new UserService(_userRepository);
            var registeredUser = userService.Register(
                "[email protected]",
                "Jesse",
                "Jesse");

            var userFromRepository = _userRepository.GetById(registeredUser.Id);
            userFromRepository.Should().NotBe(null);

            Action action = () => userService.Register(
                "[email protected]",
                "Jesse",
                "Jesse");
            action.ShouldThrow
();
        }
    }
}
namespace RepositoryAndEf.Data
{
    public class MockRepository
: IRepository
where T : BaseEntity
    {
        private List
_list = new List
();

        public T GetById(Guid id)
        {
            return _list.FirstOrDefault(e => e.Id == id);
        }

        public IEnumerable
Get(Expression
> predicate)
        {
            return _list.Where(predicate.Compile());
        }

        public bool Insert(T entity)
        {
            if (GetById(entity.Id) != null)
                throw new InvalidCastException("The id has already existed");
            _list.Add(entity);
            return true;
        }

        public bool Update(T entity)
        {
            var existingEntity = GetById(entity.Id);
            if (existingEntity == null)
                throw new InvalidCastException("Cannot find the entity.");
            existingEntity = entity;
            return true;
        }

        public bool Delete(T entity)
        {
            var existingEntity = GetById(entity.Id);
            if (existingEntity == null)
                throw new InvalidCastException("Cannot find the entity.");
            _list.Remove(entity);
            return true;
        }
    }
}
private readonly IRepository
_userRepository;
private List
_userList = new List
();
public UserServiceTests()
{
    var mockRepository = new Mock
>();
    mockRepository.Setup(r => r.Insert(It.IsAny
())).Returns((User user) =>
    {
        if (_userList.Any(u => u.Id == user.Id))
            throw new InvalidCastException("The id has already existed");
        _userList.Add(user);
        return true;
    });
    _userRepository = mockRepository.Object;
}

Finally, the article outlines criteria for good unit tests—automation, speed, isolation, clear naming, single‑case focus—and offers architectural advice for making code more testable, such as using layered architecture, dependency injection, and programming to interfaces rather than concrete implementations.

It concludes by emphasizing that while writing unit tests may seem time‑consuming, well‑designed tests provide long‑term value by catching regressions early and improving overall software quality.

software architectureC++unit testingmockingtest-driven development.NET
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.