DEV Community

Sabin Sim
Sabin Sim

Posted on

C#.NET - day 08

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<IHelloRepository>();
        var service = new HelloService(mockRepo.Object);

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

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

        Assert.Equal("Hello, TestUser!", result.Message);
    }
}
Enter fullscreen mode Exit fullscreen mode


`


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

This 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 is becoming more challenging, but at the same time it feels more organized.
  • I am starting to sense how a system behaves like a factory, where each component plays a specific role and works together.

Top comments (1)

Collapse
 
peacebinflow profile image
PEACEBINFLOW

This is a really solid way to frame it — especially for people early in C# who keep hearing “use interfaces” without ever seeing why.

You nailed the core point: interfaces aren’t about abstraction trophies, they’re about control. The moment you can swap a real dependency for a mock and prove behavior in isolation, the whole thing clicks. No DB, no side effects, no guessing — just “did this service do what it promised to do?”

I also like that you focused the test on interaction, not implementation. Verifying Save was called exactly once is a very grown-up test choice. It teaches that unit tests are about behavioral contracts, not re-running the same logic twice.

The factory analogy at the end is spot on too. Once responsibilities are clear and boundaries are enforced, systems stop feeling fragile and start feeling… predictable. That’s when testing stops being scary and starts being boring (in the best way).

Great progress for day 08 — this is one of those concepts that quietly levels people up without them realizing it.