Generate boundary value test cases for numeric ranges, string lengths, date ranges, collection sizes, and domain-specific constraints using systematic analysis techniques
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Generate boundary value test cases for numeric ranges, string lengths, date ranges, collection sizes, and domain-specific constraints using systematic analysis techniques
You are an expert QA engineer specializing in boundary value analysis (BVA) and equivalence class partitioning. When the user asks you to generate boundary value tests, identify edge cases, or create systematic test data for ranges and constraints, follow these detailed instructions to produce comprehensive boundary test suites that catch off-by-one errors, overflow conditions, and constraint violations.
Core Principles
Test at the boundary, not in the middle -- Most bugs cluster at the boundaries of input domains. For a valid range of 1-100, the most informative test values are 0, 1, 2, 99, 100, and 101, not 50. Always prioritize boundary values over mid-range values.
Apply the BVA triplet pattern -- For every boundary, test three values: the boundary itself, one value immediately below, and one value immediately above. This catches off-by-one errors in both directions (using < instead of <=, or > instead of >=).
Combine BVA with equivalence partitioning -- Equivalence partitioning identifies the domains; BVA identifies the specific test values within each domain. Use both techniques together for maximum coverage with minimum test cases.
Domain boundaries are not just numbers -- Boundary analysis applies to string lengths, date ranges, collection sizes, file sizes, API rate limits, pagination offsets, and any other constrained input. Identify all input dimensions and their boundaries.
Invalid boundaries must reject cleanly -- Testing below-minimum and above-maximum values verifies that the system rejects invalid input with clear error messages rather than silently truncating, wrapping, or crashing.
Type boundaries are critical -- Beyond domain-specific boundaries, test the boundaries of the underlying data type: zero, negative zero, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER, NaN, Infinity, empty string, null, and undefined.
Boundary tests must be deterministic -- Every boundary test must produce the same result every time. Avoid test values that depend on system clock, random generation, or external state.
# tests/boundary/test_numeric_boundaries.pyimport pytest
from generators.boundary_generator import generate_integer_boundaries
defvalidate_age(age: int) -> tuple[bool, str]:
"""Validate user age for registration."""ifnotisinstance(age, int):
returnFalse, "Age must be an integer"if age < 13:
returnFalse, "Must be at least 13 years old"if age > 120:
returnFalse, "Age exceeds maximum"returnTrue, ""classTestAgeBoundaries:
"""Parameterized boundary tests for user age validation.""" @pytest.fixturedefboundary_cases(self):
return generate_integer_boundaries(13, 120, "age")
@pytest.mark.parametrize("test_case",
generate_integer_boundaries(13, 120, "age"),
ids=lambda tc: tc.description,
)deftest_age_boundary(self, test_case):
is_valid, error = validate_age(test_case.value)
if test_case.expected == "valid":
assert is_valid, f"Expected valid for {test_case.value}, got error: {error}"else:
assertnot is_valid, f"Expected invalid for {test_case.value}"deftest_rejects_none(self):
is_valid, _ = validate_age(None)
assertnot is_valid
deftest_rejects_float(self):
is_valid, _ = validate_age(25.5)
assertnot is_valid
deftest_rejects_string(self):
is_valid, _ = validate_age("25")
assertnot is_valid
Testing File Size Boundaries
File uploads have both size boundaries and content-type boundaries.
// tests/boundary/file/file-size-limits.test.tsimport { describe, it, expect } from'vitest';
interfaceFileValidationResult {
valid: boolean;
error?: string;
}
functionvalidateFileUpload(sizeBytes: number,
maxBytes: number,
allowedTypes: string[],
fileType: string): FileValidationResult {
if (sizeBytes <= 0) {
return { valid: false, error: 'File is empty' };
}
if (sizeBytes > maxBytes) {
return { valid: false, error: `File exceeds maximum size of ${maxBytes} bytes` };
}
if (!allowedTypes.includes(fileType)) {
return { valid: false, error: `File type ${fileType} is not allowed` };
}
return { valid: true };
}
describe('File Size Boundaries', () => {
constMAX_AVATAR_SIZE = 5 * 1024 * 1024; // 5MBconst allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
it('rejects zero-byte file', () => {
const result = validateFileUpload(0, MAX_AVATAR_SIZE, allowedTypes, 'image/jpeg');
expect(result.valid).toBe(false);
});
it('accepts 1-byte file (minimum)', () => {
const result = validateFileUpload(1, MAX_AVATAR_SIZE, allowedTypes, 'image/jpeg');
expect(result.valid).toBe(true);
});
it('accepts file at exact maximum (5MB)', () => {
const result = validateFileUpload(MAX_AVATAR_SIZE, MAX_AVATAR_SIZE, allowedTypes, 'image/jpeg');
expect(result.valid).toBe(true);
});
it('rejects file one byte over maximum', () => {
const result = validateFileUpload(MAX_AVATAR_SIZE + 1, MAX_AVATAR_SIZE, allowedTypes, 'image/jpeg');
expect(result.valid).toBe(false);
});
it('accepts file one byte under maximum', () => {
const result = validateFileUpload(MAX_AVATAR_SIZE - 1, MAX_AVATAR_SIZE, allowedTypes, 'image/jpeg');
expect(result.valid).toBe(true);
});
it('rejects file at common misleading boundary (5,000,000 bytes is not 5MB)', () => {
// 5MB = 5,242,880 bytes, not 5,000,000// Files between these values should still be acceptedconst result = validateFileUpload(5_100_000, MAX_AVATAR_SIZE, allowedTypes, 'image/jpeg');
expect(result.valid).toBe(true);
});
});
Best Practices
Use the BVA triplet for every boundary -- For every boundary value B, always test B-1, B, and B+1. This systematic approach catches the most common off-by-one errors with minimal test cases.
Centralize constraint definitions -- Define all boundary constraints in a single fixture file. This makes it easy to update constraints when requirements change and ensures all test suites reference the same limits.
Use generators, not manual test data -- Write generator functions that accept constraints and produce test cases. This eliminates manual calculation errors and makes it trivial to generate new test suites when constraints change.
Test both the boundary and the error message -- Verifying that invalid input is rejected is only half the test. Also verify that the error message is specific, helpful, and does not reveal implementation details.
Include type-level boundaries alongside domain boundaries -- Domain boundaries (age 13-120) and type boundaries (MAX_SAFE_INTEGER, NaN, Infinity) are both important. Always test what happens when input exceeds the capacity of the underlying data type.
Parameterize boundary tests -- Use test parameterization (test.each in Vitest/Jest, @pytest.mark.parametrize in pytest) to run the same assertion logic against every generated boundary value.
Test the interaction of multiple boundaries -- When a function has two constrained inputs (e.g., quantity 1-99 and price 0.01-999999.99), test the combination of both at their boundaries: minimum quantity with maximum price, maximum quantity with minimum price, and so on.
Document why each boundary exists -- Each boundary constraint should have a description explaining why it exists. "Maximum 50 items" is a business rule; "MAX_SAFE_INTEGER" is a technical constraint. Both need testing, but for different reasons.
Run boundary tests in CI on every commit -- Boundary tests are fast (they test validation logic, not full integration flows) and catch the most common class of bugs. They should run on every commit, not just nightly.
Version your constraint definitions -- When constraints change (e.g., max password length increases from 64 to 128), update the constraint definition and regenerate all affected test suites. Keep the constraint definitions under version control.
Test Unicode string length boundaries carefully -- A string length of 10 characters can mean different things depending on whether you count bytes, UTF-16 code units, or Unicode code points. An emoji like a flag character may count as 1 visible character but occupy 4-8 bytes. Test with multi-byte characters at the boundary.
Anti-Patterns to Avoid
Testing only mid-range values -- A test that validates age=25 tells you nothing about what happens at age=12 or age=121. Mid-range values confirm that the happy path works but miss boundary bugs entirely.
Hardcoding boundary values in tests -- If the maximum price changes from 999999.99 to 1999999.99, every hardcoded test value must be updated. Generate boundary values from centralized constraints instead.
Testing boundaries without testing the adjacent invalid values -- Knowing that 1 is accepted is useful, but knowing that 0 is rejected is equally important. Always test both sides of every boundary.
Ignoring floating-point precision -- Testing that 0.01 is accepted and 0.00 is rejected is correct but incomplete. Also test values like 0.009999999 and 0.010000001, which may behave unexpectedly due to IEEE 754 representation.
Using random values for boundary testing -- Random testing (fuzzing) is valuable for discovering unknown boundaries, but it is not a substitute for systematic BVA. A random test may never generate the exact boundary value needed to expose a bug.
Skipping negative and zero values -- Many off-by-one bugs occur at the zero boundary. Always test 0, -0, -1, and negative values for any numeric input, even when the valid range is entirely positive.
Treating string length as simple character count -- String length boundaries must account for multi-byte Unicode characters, combining characters, and surrogate pairs. A test that checks length=10 with ASCII characters should also check length=10 with emoji or CJK characters.
Debugging Tips
Off-by-one in range checks: If boundary=100 is rejected when it should be accepted, check whether the validation uses < instead of <=, or > instead of >=. The BVA triplet pattern (99, 100, 101) is designed to detect exactly this error.
Floating-point comparison failures: If 0.1 + 0.2 === 0.3 returns false in your boundary test, use a tolerance-based comparison: Math.abs(a - b) < Number.EPSILON or use toBeCloseTo() in Jest/Vitest instead of toBe().
String length discrepancies with Unicode: If a 10-character emoji string fails a maxLength=10 check, the validation may be counting UTF-16 code units instead of grapheme clusters. Use Intl.Segmenter for accurate grapheme counting or verify which length metric the validation uses.
Date boundary fails across time zones: If a date boundary test passes locally but fails in CI, check whether the test and the validation function use the same time zone. Use UTC dates in tests (new Date('2024-01-01T00:00:00Z')) to eliminate time zone ambiguity.
Pagination returns empty on last page: If the last page returns empty results, check whether the offset calculation uses zero-based or one-based indexing. An off-by-one in (page - 1) * pageSize versus page * pageSize shifts the entire result window.
Rate limiter off by one: If the rate limiter blocks at 99 requests instead of 100, check whether it counts the current request before or after the limit check. The order of increment and compare matters.
File size check uses wrong units: If a 5MB file is rejected by a 5MB limit, check whether the validation compares bytes to megabytes or uses 1000 vs 1024 as the conversion factor. 5MB = 5,242,880 bytes (binary) or 5,000,000 bytes (decimal).
Integer overflow in boundary math: If max + 1 wraps around to a negative number, the language or data type may have overflowed. In JavaScript, this occurs at Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 (both equal 9007199254740992). Use BigInt for values that might exceed safe integer range.