C#.NET - day 08

Published: (January 13, 2026 at 01:04 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

Day 08 : Unit Testing — The Real Reason Interfaces Exist

Proving service behavior without repositories or databases

Introduction

Many tutorials introduce interfaces and immediately move on to databases.
At that point, learners often ask:

“Why did we even create this interface?”

This step answers that question. The real purpose of the interface appears when we start writing unit tests.

🧪 Step : The Proof

The reason we created IHelloRepository was not abstraction for its own sake. It was to make one thing possible:

testing business logic in isolation.

  • We want to test the behavior of HelloService.
  • We do not want to touch memory, databases, or storage logic.
  • We want tests that are fast, predictable, and repeatable.

This is only possible because the service depends on an interface, not a concrete class.

🛠 Preparing the Test Project

A separate test project is created alongside the main application.

  • Test framework: xUnit
  • Mocking library: Moq
  • Reference: the main HelloFlow project

Moq allows us to create fake implementations of interfaces with minimal effort.

🧠 What Are We Actually Testing?

This test verifies a very specific behavior:

When HelloService.GetHello() is called, does it request the repository to save the data exactly once?

We are not testing storage.
We are not testing LINQ.
We are testing behavior.

🧪 Unit Test Code

using HelloFlow.Models;
using HelloFlow.Repositories;
using HelloFlow.Services;
using Moq;
using Xunit;

namespace HelloFlow.Tests;

public class HelloServiceTests
{
    [Fact]
    public void GetHello_Should_Call_Save_In_Repository()
    {
        // Arrange
        var mockRepo = new Mock();
        var service = new HelloService(mockRepo.Object);

        // Act
        var result = service.GetHello("TestUser");

        // Assert
        mockRepo.Verify(
            x => x.Save(It.IsAny()),
            Times.Once
        );

        Assert.Equal("Hello, TestUser!", result.Message);
    }
}

🔍 Test Structure: Arrange → Act → Assert

All professional unit tests follow the same structure:

  • Arrange: prepare the environment and dependencies
  • Act: execute the method under test
  • Assert: verify the expected behavior

This structure scales from small services to large systems.

🧠 Why This Test Matters

This single test proves several important things:

  • Isolation: The service is tested without executing any repository code.
  • Speed: No database, no I/O, no setup cost.
  • Safety: Repository implementations can change without breaking service tests.

It is the foundation of automated testing in CI/CD pipelines and a key concept in DevOps practices.

🧠 One‑Sentence Summary

This step proves that the service behaves correctly — without repositories, databases, or infrastructure.

✍️ My Notes & Reflections

  • As the system grows step by step, it becomes more challenging, but also more organized.
  • I am starting to sense how a system behaves like a factory, where each component plays a specific role and works together.
Back to Blog

Related posts

Read more »