Disclaimer: ChatGPT generated document.
Most developers instinctively write tests that prove their code works. That’s necessary — but it’s not sufficient.
Real-world software rarely fails because the “happy path” is broken. It fails because:
- users do weird things
- networks misbehave
- disks fill up
- APIs are misused
- assumptions turn out false
So modern testing is not just about correctness. It’s about behavior under stress, misuse, and failure.
This article gives a conceptual map of the major testing philosophies that revolve around one core question:
How should software behave when reality doesn’t cooperate?
Goal: Verify correct behavior for valid input.
You check the system does what it promises.
Example:
CHECK(parse_int("42") == 42);
CHECK(connect(valid_server));
CHECK(add(2, 3) == 5);Positive tests answer:
Does the implementation match the specification?
They are essential — but dangerously comforting.
Passing positive tests only proves the demo works.
Goal: Verify correct behavior for invalid input.
Instead of asking “does it work?”, you ask:
Does it fail properly?
Example:
CHECK_THROWS(parse_int("abc"));
CHECK_THROWS(connect("256.256.256.256"));
CHECK_THROWS(add_overflow(INT_MAX, 1));Correct failure means:
- no crash
- no undefined behavior
- no corrupted state
- correct error reporting
Most production bugs live here — the error paths.
These inputs are technically valid… but risky.
parse_int("0");
parse_int("-2147483648");
parse_int("2147483647");
reserve(size_t(-1));Edge cases sit between success and failure:
| Type | Input |
|---|---|
| Positive | normal |
| Edge | extreme but valid |
| Negative | invalid |
Many catastrophic failures come from boundaries, not invalid input.
Now we stop blaming the user and start blaming reality.
We simulate conditions where the program still should behave safely:
- timeouts
- partial reads
- packet loss
- unavailable services
- inconsistent state
Example:
disconnect_mid_transfer();
timeout_during_handshake();
half_open_connection();The system may not succeed — but it must remain sane.
Fuzzing feeds random data into your program:
""
"\0\0\0\0"
"AAAAAAAAAAAA"
random bytes
broken packets
Goal:
Discover failures you didn’t imagine.
Fuzzing is essentially infinite negative testing performed by a machine with no assumptions.
It finds:
- crashes
- memory corruption
- UB
- parser flaws
Many major security bugs are discovered this way.
Instead of invalid input, we simulate invalid reality.
We force system calls to fail:
- allocation failure
- disk full
- DNS failure
- clock jumps
- packet loss
Example mindset:
What if
mallocfails here?
If your code cannot survive that question, it’s fragile.
Fault injection in development becomes chaos engineering in production.
You intentionally break the running system:
- kill servers
- add latency
- drop requests
- partition networks
Goal:
The system must degrade gracefully, not catastrophically.
Reliable systems are not those that never fail — they are those that fail predictably.
Security testing is specialized failure testing.
The inputs are not just wrong — they are hostile.
Examples:
- buffer overflow attempts
- injection attacks
- integer wraparound
- path traversal
Here the tester behaves like an attacker.
We can group all these approaches into two mental models:
“Does the feature work?”
“What happens when assumptions collapse?”
Reliable software comes from the second mindset.
Users explore functionality. Reality explores failure modes.
| Category | Question answered |
|---|---|
| Positive testing | Does it work? |
| Negative testing | Does it fail correctly? |
| Boundary testing | Are limits handled? |
| Robustness testing | Can it survive bad conditions? |
| Fuzz testing | What failures did we miss? |
| Fault injection | What if dependencies fail? |
| Chaos testing | Can the system handle real outages? |
| Security testing | Can an attacker break it? |
Bad software breaks when something unexpected happens.
Good software reports an error.
Great software continues operating safely.
So testing maturity progresses like this:
- Prove correctness
- Prove safe failure
- Prove resilience
- Prove survivability
Testing is not about proving your code works.
It’s about proving the system remains trustworthy when the world stops behaving.
Because users don’t live in the happy path — they live in reality.
