C#.NET - day 08
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
HelloFlowproject
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.