Look, I write tests. I'm not a monster.
But here's what I realized: Every test I write manually comes from my brain. And my brain has blind spots.
I test the happy path. I test the obvious edge cases. Maybe I remember to test null values if I'm being thorough.
But there are hundreds of edge cases I'll never think of. Because I'm the person who wrote the code. I have unconscious assumptions about how it'll be used.
That's where the bugs hide. In the scenarios you didn't imagine.
Here's What Changed
I started asking AI to generate tests for my code. Not just "write tests" - but specifically "find edge cases I didn't think of."
And holy hell, it finds them.
AI doesn't have my assumptions. It doesn't know what I "meant" the code to do. It just looks at what the code actually does and generates test cases based on that.
It's like fuzzing, but smarter. Instead of random inputs, you get targeted test cases that explore realistic scenarios you forgot existed.
What You Get
- Hundreds of test cases in seconds - Coverage you'd never write manually
- Edge cases you forgot existed - Empty strings, negative numbers, Unicode, timezone weirdness
- Realistic failure scenarios - Not random garbage, actual edge cases users hit
- Bugs found before production - Catch issues in development, not at 3am
- Zero bias - AI doesn't know what you "intended," only what the code does
How This Actually Works
Time to try this: 2 minutes
The secret is in how you prompt the AI. Don't ask it to "write tests." Ask it to find your blind spots.
Step 1: Ask AI to Generate Edge Cases (Not Tests)
Instead of "write tests," try this:
Me: "Look at this function. Generate 20 edge cases that might break it.
Include weird inputs I wouldn't think to test for."
AI: *generates comprehensive list of edge cases*
- Empty string
- String with only whitespace
- String with emoji/Unicode
- String exceeding max length
- Null vs undefined
- Array with one item
- Array with duplicates
- Negative numbers
- Zero
- Infinity
- NaN
- Date at epoch
- Date in far future
- Timezone edge cases
- Malformed JSON
- Circular references
...
Now you have a list of cases you probably wouldn't have tested. AI generated them because it doesn't share your assumptions.
Step 2: Have AI Write Tests for Those Cases
Me: "Write tests for all of these edge cases."
AI: *generates comprehensive test suite covering all scenarios*
Run the tests. Watch stuff fail. Fix the bugs before users find them.
Step 3: Iterate on Failures
The magic happens when tests fail:
*Test fails: Function crashes on empty array*
Me: "This test is failing. Should the function handle empty arrays?
If yes, fix it. If no, explain why and document the assumption."
AI: *either fixes the bug OR documents why it's expected behavior*
You end up with either more robust code or better documentation of assumptions. Both are wins.
Real Example: Email Validation
I wrote an email validator. Tested it with valid emails, invalid emails, empty string. Shipped it.
Then I asked AI to generate edge cases:
Me: "Generate 30 email validation edge cases I should test."
AI generated:
- email@domain (no TLD)
- @domain.com (no local part)
- email@.com (domain starts with dot)
- email@domain..com (consecutive dots)
- email with spaces@domain.com
- email@domain.com. (trailing dot)
- "quoted"@domain.com (quoted local part - valid!)
- email@[192.168.1.1] (IP address domain - valid!)
- email@domain.co.uk (multi-part TLD)
- very.long.email.address.with.many.dots@domain.com
- email+tag@domain.com (plus addressing)
- email@localhost (no domain)
- üñíçø∂é@domain.com (Unicode local part)
- email@üñíçø∂é.com (Unicode domain/IDN)
...
My validator failed on like 8 of these. Some were bugs (it crashed on quoted strings). Some were design decisions I needed to make (do we support IP addresses as domains?).
I would NEVER have tested half of these manually. Because I don't think about edge cases like "what if the domain is an IP address in brackets?"
But users do that. And my code needs to handle it (or explicitly reject it with a clear error).
Why This Is Like Fuzzing But Better
Traditional fuzzing throws random inputs at your code to find crashes. It's effective but noisy - lots of garbage inputs that aren't realistic.
AI-generated tests are like intelligent fuzzing:
- Targeted - Tests realistic edge cases, not random garbage
- Comprehensive - Covers categories of inputs (nulls, empties, boundaries, Unicode, etc.)
- Readable - You get actual test cases you can review and maintain
- Fast - Hundreds of test cases in seconds, no waiting for fuzzer to run
You're fuzzing the test space, not the input space. Finding coverage gaps instead of crashes.
The Prompts I Actually Use
For new code:
"Generate 25 edge cases for this function. Include:
- Boundary conditions (empty, zero, max values)
- Type edge cases (null, undefined, NaN, Infinity)
- Format edge cases (Unicode, special chars, whitespace)
- Realistic user mistakes
- Cases that violate assumptions in the code"
For existing code with tests:
"Look at this function and its existing tests.
What edge cases am I NOT testing? Generate 15 more cases."
For integration/API code:
"Generate edge cases for this API endpoint:
- Malformed requests
- Missing required fields
- Wrong types
- Boundary values
- Race conditions
- Timeout scenarios
- Partial failures"
For refactoring:
"I'm refactoring this code. Generate tests that verify:
1. Current behavior is preserved
2. Edge cases still work
3. Error handling stays the same
Focus on cases that might break during refactoring."
What I Learned From My Blind Spots
Pattern 1: I always forget about whitespace
AI consistently generates tests like "input is just spaces" or "input has trailing newlines." I NEVER think to test these manually.
Turns out lots of bugs hide in whitespace handling.
Pattern 2: I underestimate Unicode
Emoji. Accented characters. Right-to-left text. AI generates tests for all of these.
I would test "hello" and think I'm done. AI tests "🎉" and "こんにちは" and exposes my broken string handling.
Pattern 3: I assume users are reasonable
AI doesn't. It generates tests where users pass objects instead of strings, negative numbers where only positive makes sense, dates from the year 1970.
These aren't theoretical. These are real inputs from real users who misunderstood the API.
Pattern 4: I forget about the sad path
I test success cases thoroughly. AI reminds me to test: network failures, timeouts, partial responses, rate limiting, auth expiry, database connection loss...
All the stuff that happens in production but never in development.
"But I Don't Want to Maintain 100 Tests"
Fair point. Here's what I do:
Option 1: Keep the ones that fail
Generate 50 test cases. Run them. Keep only the ones that found bugs or revealed assumptions.
If 30 tests pass immediately, you might only need to keep 5-10 that actually added value.
Option 2: Parameterized tests
Instead of 50 separate tests, make one test with 50 inputs:
test.each([
{ input: "", expected: false },
{ input: " ", expected: false },
{ input: "test@example.com", expected: true },
{ input: "🎉@example.com", expected: false },
// ... 46 more cases
])('validates email: $input', ({ input, expected }) => {
expect(validateEmail(input)).toBe(expected);
});
Easy to maintain. Documents all the edge cases in one place.
Option 3: Snapshot testing
For complex outputs, generate inputs and snapshot the results:
AI: *generates 100 edge case inputs*
Me: "Create a snapshot test with these inputs."
AI: *generates test that captures current behavior*
Now refactoring is safe - any behavior change gets flagged.
The Workflow That Actually Works
Takes about 5 minutes total:
1. ✅ Write your code (or open existing code)
2. 📝 Ask AI: "Generate 20 edge cases for this function"
3. 🧪 Ask AI: "Write tests for these cases"
4. ▶️ Run the tests
5. 🐛 Fix failures (or document assumptions)
6. 🎉 Ship with confidence
You're somewhat blind to the scope of your own code. That's not a weakness - it's just how human brains work.
AI doesn't have that blindness. Use it to find your gaps.
Why This Actually Matters
Every developer has blind spots. We test what we expect to break. We don't test scenarios we can't imagine.
That's where bugs live. In the intersection of "possible input" and "didn't think to test."
Before AI, closing this gap meant either:
- Hours of manual test writing (never happens)
- Fuzzing tools that need setup and generate noise
- Waiting for users to find bugs in production
Now? Ask AI to find your blind spots. Takes 2 minutes. Generates realistic edge cases. Finds bugs before users do.
The developers who ship the most reliable code aren't the ones who never make mistakes. They're the ones who test scenarios they didn't think of.
AI makes that trivial.
Try It Right Now
Takes 90 seconds:
1. Open a function you wrote recently
2. Ask your AI tool: "Generate 15 edge cases that might break this"
3. Read the list - you'll immediately spot cases you forgot
4. Ask: "Write tests for these cases"
5. Run them and see what breaks
You'll find at least one bug. Or you'll discover an assumption you need to document. Either way, your code gets better.
Stop testing only what you can imagine. Let AI imagine the chaos users will create.
Your future self (debugging at 2am) will thank you.
Photo by Kelly Sikkema on Unsplash
Content on this blog was created using human and AI-assisted workflows described here. Original ideas and editorial decisions by Justin Quaintance.