| name | edge-case-generator |
| description | Automatically identify potential boundary and exception cases from requirements, specifications, or existing code, and generate comprehensive test cases targeting boundary conditions, edge cases, and uncommon scenarios. Use this skill when analyzing programs, code repositories, functions, or APIs to discover and test corner cases, null handling, overflow conditions, empty inputs, concurrent access patterns, and other exceptional scenarios that are often missed in standard testing. |
Edge Case Generator
Systematically identify boundary conditions, edge cases, and exceptional scenarios, then generate comprehensive tests to validate software behavior under uncommon conditions.
Core Capabilities
1. Edge Case Identification
Analyze code, specifications, or requirements to identify:
- Boundary values: Min/max limits, zero, empty, first/last elements
- Null and undefined: Missing data, null pointers, undefined references
- Type boundaries: Integer overflow, floating-point precision, type mismatches
- Collection edge cases: Empty collections, single element, duplicates
- State transitions: Invalid state sequences, concurrent modifications
- Resource limits: Memory exhaustion, timeout scenarios, disk full
- Special characters: Unicode, whitespace, control characters
- Concurrency issues: Race conditions, deadlocks, thread safety
2. Test Generation
Generate tests in multiple formats:
- Unit tests (pytest, Jest, JUnit, Go testing, etc.)
- Integration test scenarios
- Property-based tests (Hypothesis, QuickCheck, fast-check)
- Mutation testing scenarios
- Fuzz testing inputs
3. Multi-Language Support
Support major programming languages:
- Python, JavaScript/TypeScript, Java, C/C++, Go, Rust, C#, Ruby, PHP, Swift, Kotlin
Edge Case Analysis Workflow
Step 1: Identify Input Domains
Analyze each input parameter or data structure:
For numeric inputs:
- Minimum value (e.g., INT_MIN, 0, -∞)
- Maximum value (e.g., INT_MAX, 255, +∞)
- Zero
- One (unit value)
- Negative one
- Just below/above boundaries
- Overflow/underflow values
For collections (arrays, lists, sets):
- Empty collection
- Single element
- Two elements (minimal interaction)
- Maximum size (if bounded)
- All identical elements
- All unique elements
- Sorted vs unsorted
- Contains duplicates
For strings:
- Empty string ("")
- Single character
- Very long string (10K+ chars)
- Unicode characters (emoji, RTL, special)
- Whitespace only (" ")
- Null terminator issues
- Special characters (\n, \t, \0)
For pointers/references:
- Null/None/undefined
- Dangling pointer
- Self-reference (circular)
- Uninitialized
Step 2: Identify State and Preconditions
Object state:
- Uninitialized state
- Partially initialized
- Valid operational state
- Invalid/corrupted state
- Locked/busy state
- Disposed/freed state
Precondition violations:
- Missing required parameters
- Parameters in wrong order
- Invalid combinations
- Violated invariants
Step 3: Identify Output Scenarios
Success cases:
- Typical successful execution
- Boundary successful case
- Empty result (valid but empty)
Failure cases:
- Expected exceptions/errors
- Timeout scenarios
- Partial failures
- Resource exhaustion
Step 4: Identify Interaction Patterns
Temporal patterns:
- First operation
- Last operation
- Repeated operations
- Alternating operations
- Concurrent operations
Dependency patterns:
- Missing dependencies
- Circular dependencies
- Incompatible versions
- Network failures
Step 5: Generate Test Cases
For each identified edge case, generate:
- Test name: Descriptive name indicating the edge case
- Setup: Initialize necessary state/fixtures
- Input: The specific boundary or edge case input
- Expected behavior: What should happen (success, specific exception, etc.)
- Assertions: Verify the expected behavior
- Cleanup: Restore state if necessary
Edge Case Categories
Category 1: Numeric Boundaries
def test_factorial_edge_cases():
assert factorial(0) == 1
assert factorial(1) == 1
with pytest.raises(ValueError):
factorial(-1)
with pytest.raises(OverflowError):
factorial(10000)
with pytest.raises(TypeError):
factorial(3.5)
Category 2: Collection Boundaries
describe('processArray edge cases', () => {
test('empty array', () => {
expect(processArray([])).toEqual([]);
});
test('single element', () => {
expect(processArray([1])).toEqual([1]);
});
test('null input', () => {
expect(() => processArray(null)).toThrow(TypeError);
});
test('undefined input', () => {
expect(() => processArray(undefined)).toThrow(TypeError);
});
test('all identical elements', () => {
expect(processArray([5, 5, 5, 5])).toEqual([5, 5, 5, 5]);
});
test(, {
largeArray = ().();
( (largeArray))..();
});
});
Category 3: String Boundaries
@Test
public void testValidateString_EdgeCases() {
assertThrows(ValidationException.class, () -> validateString(""));
assertThrows(NullPointerException.class, () -> validateString(null));
assertTrue(validateString("a"));
assertFalse(validateString(" "));
assertTrue(validateString("hello@world!"));
assertTrue(validateString("你好世界"));
String longString = "a".repeat(100000);
assertDoesNotThrow(() -> validateString(longString));
assertFalse(validateString("hello\0world"));
}
Category 4: Pointer/Reference Boundaries
void test_pointer_edge_cases() {
assert(safe_strlen(NULL) == -1);
assert(safe_strlen("") == 0);
assert(safe_strlen("a") == 1);
char long_str[10000];
memset(long_str, 'a', 9999);
long_str[9999] = '\0';
assert(safe_strlen(long_str) == 9999);
}
Category 5: Concurrency Edge Cases
func TestConcurrentAccess(t *testing.T) {
cache := NewCache()
t.Run("concurrent writes", func(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(val int) {
defer wg.Done()
cache.Set("key", val)
}(i)
}
wg.Wait()
_, ok := cache.Get("key")
assert.True(t, ok)
})
t.Run("potential deadlock", func(t *testing.T) {
done := make(chan bool)
go func() {
cache.Set("key1", 1)
cache.Set("key2", 2)
done <- true
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("Deadlock detected")
}
})
}
Category 6: State Transition Edge Cases
def test_state_machine_edge_cases():
machine = StateMachine()
with pytest.raises(InvalidStateError):
machine.stop()
machine.start()
with pytest.raises(InvalidStateError):
machine.start()
machine.trigger_error()
with pytest.raises(InvalidStateError):
machine.resume()
machine.reset()
machine.start()
machine.pause()
machine.resume()
machine.stop()
assert machine.state == "stopped"
Category 7: Resource Limit Edge Cases
def test_resource_limits():
with pytest.raises(MemoryError):
allocate_huge_buffer(size=10**15)
handles = []
try:
for i in range(10000):
handles.append(open(f'/tmp/test_{i}', 'w'))
except OSError as e:
assert "Too many open files" in str(e)
finally:
for h in handles:
h.close()
with pytest.raises(TimeoutError):
slow_operation(timeout=0.001)
with patch('os.write', side_effect=OSError("No space left on device")):
with pytest.raises(OSError):
write_large_file()
Test Generation Patterns
Pattern 1: Equivalence Partitioning
Divide input domain into equivalence classes:
def test_age_validation():
assert not is_valid_age(-1)
assert not is_valid_age(-100)
assert is_valid_age(0)
assert is_valid_age(120)
assert is_valid_age(25)
assert is_valid_age(65)
assert not is_valid_age(121)
assert not is_valid_age(1000)
Pattern 2: Boundary Value Analysis
Test at and around boundaries:
describe('array access boundaries', () => {
const arr = [10, 20, 30, 40, 50];
test('before first element', () => {
expect(() => arr.at(-1)).toThrow(RangeError);
});
test('first element (boundary)', () => {
expect(arr.at(0)).toBe(10);
});
test('second element (just inside)', () => {
expect(arr.at(1)).toBe(20);
});
test('last element (boundary)', () => {
expect(arr.at(4)).toBe(50);
});
test('after last element', () => {
expect(() => arr.at(5)).toThrow();
});
});
Pattern 3: Property-Based Testing
Generate random edge cases automatically:
from hypothesis import given, strategies as st
@given(st.lists(st.integers()))
def test_sort_properties(input_list):
"""Property: sorted list length equals input length"""
result = sort(input_list)
assert len(result) == len(input_list)
@given(st.lists(st.integers(), min_size=1))
def test_max_element_property(input_list):
"""Property: max element >= all elements"""
max_val = find_max(input_list)
assert all(max_val >= x for x in input_list)
@given(st.text())
def test_string_reverse_property(s):
"""Property: reversing twice returns original"""
assert reverse(reverse(s)) == s
Pattern 4: Mutation Testing
Test that tests actually catch bugs:
def is_even(n):
return n % 2 == 0
def is_even_mutant1(n):
return n % 2 != 0
def test_is_even():
assert is_even(0) == True
assert is_even(1) == False
assert is_even(2) == True
Common Edge Case Checklist
Use this checklist when analyzing code:
Numeric Inputs
Collections
Strings
Pointers/References
State/Lifecycle
Concurrency
Resources
Dependencies
Language-Specific Patterns
For language-specific edge cases and test patterns, see the reference files:
Best Practices
- Prioritize edge cases: Focus on cases most likely to cause failures
- Test both sides of boundaries: Not just at the boundary, but just before and after
- Combine edge cases: Test multiple edge conditions together
- Document assumptions: Explain what each edge case tests
- Use descriptive names: Test names should clearly indicate the edge case
- Isolate tests: Each test should be independent
- Consider performance: Some edge cases (large inputs) may be slow
- Property-based testing: Use tools like Hypothesis/QuickCheck for automatic edge case generation
Example: Complete Edge Case Analysis
Given a function specification:
def binary_search(arr: list[int], target: int) -> int:
"""
Find target in sorted array using binary search.
Returns index if found, -1 otherwise.
Requires: arr is sorted in ascending order
"""
Generated edge case tests:
import pytest
class TestBinarySearchEdgeCases:
"""Comprehensive edge case tests for binary_search"""
def test_empty_array(self):
"""Edge: Empty collection"""
assert binary_search([], 5) == -1
def test_single_element_found(self):
"""Edge: Single element - target present"""
assert binary_search([5], 5) == 0
def test_single_element_not_found(self):
"""Edge: Single element - target absent"""
assert binary_search([5], 3) == -1
def test_two_elements_first(self):
"""Edge: Minimal interaction - target is first"""
assert binary_search([1, 2], 1) == 0
def test_two_elements_second(self):
"""Edge: Minimal interaction - target is second"""
assert binary_search([1, 2], 2) == 1
def ():
binary_search([, , , , ], ) ==
():
binary_search([, , , , ], ) ==
():
binary_search([, , ], ) == -
():
binary_search([, , ], ) == -
():
arr = [, , , , ]
binary_search(arr, ) ((arr))
():
result = binary_search([, , , , ], )
result [, , ]
():
binary_search([-, -, , , ], -) ==
():
binary_search([-, -, , , ], ) ==
():
sys
max_int = sys.maxsize
binary_search([, max_int], max_int) ==
():
arr = (())
binary_search(arr, ) ==
():
pytest.raises(TypeError):
binary_search(, )
():
result = binary_search([, , , , ], )
(result, )
This comprehensive test suite covers 18 distinct edge cases across multiple categories.