Unit testy w Infrastructure .NET – jak testować File.ReadAllText zgodnie z Clean Architecture
Dlaczego Infrastructure psuje testy?
Testowanie warstwy Infrastructure w .NET to temat, który regularnie wywołuje frustrację. Domain? Czysta logika. Application? Use case’y i mocki. Ale gdy pojawiają się pliki, JSON i File.ReadAllText, większość projektów kapituluje.
Efekt?
- testy zależne od środowiska,
- brak izolacji,
- tzw. unit testy, które w rzeczywistości są testami integracyjnymi.
W tym artykule pokażę Ci jak pisać prawdziwe unit testy w Infrastructure, bez kombinowania i bez łamania zasad Clean Architecture.
Problem: File.ReadAllText niszczy izolację testów

Dlaczego File.ReadAllText to problem testowy?
File.ReadAllText:
- jest statycznym API,
- ma twardą zależność od systemu plików,
- nie da się go mockować.
var json = File.ReadAllText(path);
To oznacza, że:
- test wymaga prawdziwego pliku,
- wynik zależy od środowiska,
- test przestaje być deterministyczny.
➡️ To nie jest unit test. To test integracyjny z systemem plików.
Clean Architecture a Infrastructure – gdzie jest granica?

W Clean Architecture obowiązuje prosta zasada:
- Domain i Application nie wiedzą nic o szczegółach technicznych
- Infrastructure zna szczegóły, ale nadal zależy od abstrakcji
Nie walczymy z File.ReadAllText() Nie piszemy własnego file systemu.
👉 Owijamy zależność w interfejs.
To jest dokładnie ta granica, o którą chodzi w dobrej architekturze.
Krok 1: Minimalna abstrakcja – IFileReader
Tworzymy najprostszą możliwą abstrakcję:
public interface IFileReader
{
string ReadAllText(string path);
}
✔ jedna metoda
✔ zero logiki
✔ jednoznaczna odpowiedzialność
To nie jest overengineering. To punkt odcięcia zależności.

Krok 2: Implementacja produkcyjna w Infrastructure
public class FileReader : IFileReader
{
public string ReadAllText(string path)=> File.ReadAllText(path);
}
📌 To jedyne miejsce w całej aplikacji, które zna system plików.
Krok 3: Refaktoryzacja repozytorium
Repozytorium przestaje znać File.
public class JsonPostacRepository : IPostacRepository
{
private readonly IPostacFactory _postacFactory;
private readonly IFileReader _fileReader;
public JsonPostacRepository(IPostacFactory postacFactory, IFileReader fileReader)
{
_postacFactory = postacFactory;
_fileReader = fileReader;
}
public List<Postac> PobierzPostacie(string sciezka)
{
var json = _fileReader.ReadAllText(sciezka);
var configList = JsonSerializer.Deserialize<List<PostacDto>>(json);
var lista = new List<Postac>();
foreach (var cfg in configList)
{
if (cfg.Kategoria.ToLower() == "bohater")
lista.Add(_postacFactory.StworzBohatera(cfg.Typ, cfg.Imie));
else if (cfg.Kategoria.ToLower() == "potwor")
lista.Add(_postacFactory.StworzPotwora(cfg.Typ));
}
return lista;
}
}
Co zyskaliśmy?
- repozytorium nie zna systemu plików
- zależy wyłącznie od interfejsów
- Infrastructure stała się testowalna
Krok 4: Rejestracja w Dependency Injection
services.AddScoped<IFileReader, FileReader>();
Produkcja → prawdziwy plik
Testy → mock
Krok 5: Unit test Infrastructure (bez plików)

public class JsonPostacRepositoryTests
{
[Fact]
public void PobierzPostacie_WithValidJson_ShouldReturnCharacters()
{
// Arrange
var json = """
[
{ "Kategoria": "Bohater", "Typ": "Wojownik", "Imie": "Aragorn" },
{ "Kategoria": "Potwor", "Typ": "Ork" }
]
""";
var fileRedearMock = new Mock<IFileReader>();
fileRedearMock.Setup(r => r.ReadAllText("path"))
.Returns(json);
var aragon = new Wojownik("Aragorn");
var ork = new Ork();
var factoryMock = new Mock<IPostacFactory>();
factoryMock.Setup(f => f.StworzBohatera("Wojownik", "Aragorn")).Returns(aragon);
factoryMock.Setup(f => f.StworzPotwora("Ork")).Returns(ork);
var repository = new JsonPostacRepository(
factoryMock.Object,
fileRedearMock.Object);
// Act
var result = repository.PobierzPostacie("path");
// Assert
result.Should().HaveCount(2);
result.Should().Contain(aragon);
result.Should().Contain(ork);
}
}
Co testujemy naprawdę?
✔ logikę repozytorium
✔ mapowanie JSON → domena
✔ współpracę zależności
❌ system plików
❌ środowisko
To jest czysty unit test.
Checklist: kiedy stosować wrappery?
- ⛔ statyczne API
- ⛔ zależność od I/O
- ⛔ trudność w mockowaniu
➡️ owijaj w interfejs
Co dalej?
- testy błędnego JSON-a
- obsługa wyjątków
- walidacja danych
- osobne testy integracyjne
Zobacz też:
👉 Zostaw komentarz – z czym masz największy problem przy testach?
👉 Sprawdź też: Unit Testing Core Layer w .NET
👉 Sprawdź też: Unit Testing Application Layer w .NET

