Software Testing and Quality Assurance

Tests usually pass for reasons unrelated to whether the code under test actually works. Line coverage reports green, the CI dashboard is clean, and a bug ships to production through a code path every test technically executed. The articles in this collection start from that observation: the value of a test is what it would catch on failure, not what it asserts on success.

Mutation testing is the recurring theme because it answers the question coverage cannot: if the implementation changes in a small, semantically meaningful way, do any tests notice? Stryker.NET introduces deliberate faults — flipping > to >=, replacing return values, removing method calls — and reports which mutations survive. Surviving mutations are blind spots dressed up as test coverage, and the surprise during the first run is rarely small.

TimeProvider adoption is the second recurring topic. DateTime.UtcNow is a hidden dependency that makes deterministic testing impossible and silently produces bugs that only surface at midnight, on the last day of the month, or across daylight saving transitions. .NET 8 shipped the abstraction in 2023; most codebases still ignore it. The articles cover how to introduce TimeProvider to legacy code without rewriting half the project and what tests become possible once time is no longer hardcoded.

Integration tests get specific attention because the failure mode is different. Unit tests prove that a method does what its author intended; integration tests prove that the wiring around it still holds. The trade-offs between in-memory fakes, Testcontainers, and full deployed environments are covered with the costs named — test execution time, flakiness budgets, and the maintenance overhead each option implies.

A separate cluster examines what tests claim to prove versus what they actually prove: contract tests that miss the contracts that matter, snapshot tests that lock in bugs as expected output, and the coverage thresholds that look rigorous in CI but mean nothing in production.

Green Dashboard, Dead Application

Green Dashboard, Dead Application

Your application just crashed in production. Azure App Service kept routing traffic to the failing instance for ninety seconds. Users saw timeouts. Your monitoring dashboard stayed green because the web server responded with HTTP 200 while the database connection pool was exhausted.

I’ve watched this exact scenario play out at three different organizations in the past year. Each time, the post-mortem revealed the same root cause: health checks that verified the process was breathing without checking whether it could actually do its job. ISO/IEC 27001 Control A.17.2.1 exists precisely for this reason—availability is a security control, not an operational afterthought.

Stop Hoarding Personal Data in Entity Framework

Stop Hoarding Personal Data in Entity Framework

The classic monolithic User entity—stuffed with birth dates, phone numbers, employment history, and marital status “just in case”—turns into a compliance nightmare the moment someone requests data deletion. You can’t delete without breaking referential integrity. You can’t keep the data without violating GDPR. You can’t anonymize without retaining fields that should never have existed. The solution isn’t complex: separate operational data from personal data, make consent-based fields nullable and purpose-documented, implement soft deletes with query filters, and validate your API boundaries with integration tests that fail when unnecessary fields leak through. Data minimization isn’t regulatory overhead—it’s architectural hygiene that makes your deletion logic straightforward and your audit responses honest.
2025 in Review: The Year .NET Stopped Lying to Itself

2025 in Review: The Year .NET Stopped Lying to Itself

Forget the hype—2025 was when .NET tooling finally stopped pretending complexity doesn’t exist Three tools won by being honest: Aspire exposed topology, TUnit killed flaky tests, Testcontainers made infrastructure real
.NET 10 Testing: Microsoft Finally Fixed the Test Runner (Mostly)

.NET 10 Testing: Microsoft Finally Fixed the Test Runner (Mostly)

.NET 10 replaces VSTest with Microsoft.Testing.Platform, bringing SDK-integrated testing with faster discovery, consistent behavior across environments, and explicit configuration contracts. But it requires .NET 10, breaks old test adapters, and demands CI pipeline discipline. Here’s what actually changes, who should migrate now, and who should wait.
Your Tests Are Lying — Mutation Testing in .NET

Tests Are Lying

It begins like many stories in software: a well-intentioned developer joining a project, determined to do things properly. You arrive at a codebase that has grown organically, perhaps even chaotically. You decide you will bring order. You set up unit testing, you configure continuous integration, you measure code coverage. You write dozens or hundreds of tests. Every public method is touched, every branch is at least executed. The dashboard lights up green. You feel, quite frankly, on top of things.

Then one day, production breaks under your watch