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.

Stop Pretending TimeProvider Doesn't Exist

Stop Pretending TimeProvider Doesn't Exist

DateTime.UtcNow looks harmless. It is not. It is a hidden dependency you cannot control in tests, cannot reproduce in staging, and cannot freeze to catch the bugs that only surface at midnight or on the last day of the month. .NET 8 shipped TimeProvider to fix this. Two years on, most codebases still ignore it. Some planned to adopt it later. Later has not arrived.
Security Tests That Prove Themselves

Security Tests That Prove Themselves

Your security tests run. They pass. But can you prove when they ran and against which code version? Most security testing lives in Word documents, Postman exports, and screenshot folders on SharePoint. The tests themselves might be valid. The evidence trail is not. This article shows how to build CLI-based test suites using xUnit and WebApplicationFactory that generate their own proof: structured logs with timestamps, commit hashes, and correlation IDs captured automatically in CI/CD pipelines. No more quarterly reports that could have been written yesterday. Instead, 847 test executions across 23 deployments, each linked to a specific commit and preserved for 90 days.
"Just Delete the User": Famous Last Words Before the GDPR Audit

"Just Delete the User": Famous Last Words Before the GDPR Audit

Your PM thinks erasure is a quick database DELETE. Three weeks later, you’ve found user data in seventeen places: production DB, analytics warehouse, Redis cache, Elasticsearch, backup tapes, and that legacy system nobody dares touch. “Delete” actually means orchestrating coordinated erasure across distributed systems, maintaining audit trails, notifying third parties, and proving it worked. This guide shows the fatal patterns I’ve seen fail spectacularly, then walks through proper orchestration with Azure Durable Functions, soft-delete with anonymization, verification checks, and immutable audit logs.
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.