Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
When Not to Mock: Over-Mocking, Brittle Tests and "Don't Mock What You Don't Own"
Mocks belong only at the system boundary, for unmanaged dependencies observable from outside; domain objects and the application's own database are not mocked. A mocked exchange rate adapter kept tests green while production showed 100 EUR as 0 TRY; an HttpMessageHandler stub caught the bug on the first run. Output, state and communication test styles and the managed vs unmanaged distinction.
Mocking Libraries in Depth: NSubstitute, Moq and FakeItEasy
NSubstitute, Moq and FakeItEasy share the same Castle DynamicProxy engine, so they can only fake interfaces and virtual members, and the differences between them are mostly syntax and failure messages. The same payment scenarios in all three: argument matching and capture, sequential returns, async exceptions, call count and order, strict mode, the NS1000 analyzer warning and Moq's 2023 SponsorLink incident.
Test Doubles: The Difference Between Dummy, Stub, Spy, Mock and Fake
A test double's kind is decided by its role in the test, not by its library: a dummy fills a slot, a stub supplies data, spies and mocks verify outgoing calls, and a fake is a working shortcut. The Meszaros taxonomy, Fowler's state vs behavior verification, why asserting on stubs hurts, how a fake that does not copy records hid a missing SaveAsync, and FakeTimeProvider and FakeLogger.
What Is the "Unit" in Unit Testing? Classical vs London School and the Four Pillars of a Good Test
A unit is a behavior, not a method or a class; the classical school, which uses test doubles only at the system boundary, produces tests that resist refactoring better than the London school, which mocks every collaborator. Solitary vs sociable tests, Khorikov's four pillars of a good test, and real output showing how a behavior-preserving change breaks a mocked test.
What to Test at Which Level: Behavior, Boundaries and Which Dependencies to Mock
Domain logic gets heavy unit testing, orchestration code a few integration tests; trivial code goes untested and overcomplicated code is refactored first. In tests your own database (managed) is real, while unmanaged dependencies such as payment and email are mocked. The real output of a mock-heavy test breaking on a behavior-preserving refactoring, risk-based prioritization and a one-page test strategy document.