Testing
What separates a senior tester from a junior: knowing when to drop mocks for real dependencies, when integration tests pay back their cost, and how to architect a test suite that gives confidence without strangling refactors.
Topics (canonical order)
-
xUnit + NUnit (when to choose), Moq + NSubstitute, fixtures, theories, fluent assertions
-
Integration Testing — WebApplicationFactory
In-memory host, service overrides, authentication in tests, custom servers
-
Real Postgres/Redis/Kafka in tests, lifetime, fixtures, CI considerations
-
Verify (snapshot), Stryker.NET (mutation), when each pays off
-
Test pyramid critique, test scopes, what NOT to test, coverage as signal vs goal
-
:material-puppet:{ .lg .middle } End-to-End with Playwright
Microsoft.Playwright for .NET, browser contexts, storage state auth, trace viewer, CI
-
Red-green-refactor, classic vs London schools, Meszaros taxonomy (stub/fake/spy/mock)
-
Property-Based Testing with FsCheck
Generators, shrinking, common properties, model-based testing, when PBT pays off
-
NetArchTest / ArchUnitNET fluent rules; layering enforcement; vs Roslyn analyzers
-
NBomber / k6 / Azure Load Testing; open-loop vs closed-loop; coordinated omission
-
Stryker.NET operators, equivalent mutants, score thresholds, incremental mode in CI
Why this order
Fundamentals first because every test depends on them. WebApplicationFactory next because it's the canonical ASP.NET Core integration entry point. Testcontainers follows because real dependencies replace mocks. Snapshot + mutation come third because they're advanced techniques that complement the basics. Test architecture closes the section because it's a synthesis topic that depends on understanding all the prior tools.
Cross-references
- WebApplicationFactory references Hosting & Kestrel and Dependency Injection.
- Testcontainers references Docker & Containers.
- Test architecture connects to Architecture & Patterns — testability is a downstream consequence of architecture choices.