Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Provides edge case, corner case, boundary condition, and limit testing patterns for Java unit tests. Validates minimum/maximum values, null cases, empty collections, numeric overflow/underflow, floating-point precision, and off-by-one scenarios using JUnit 5 and AssertJ. Use when writing .java test files to ensure code handles limits, corner cases, and special inputs correctly.
.claude/skills/giuseppe-trisciuoglio-unit-test-boundary-conditions/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 89% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 111% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 71% | 0% |
Systematic patterns for testing boundary conditions, corner cases, and limit values in Java using JUnit 5. Covers numeric boundaries, string edge cases, collection states, floating-point precision, date/time limits, and off-by-one scenarios.
@ParameterizedTest with @ValueSource or @CsvSource for multiple boundary valuesisCloseTo(expected, within(tolerance)) with AssertJMath.addExact() and Math.subtractExact() to detect arithmetic overflowRequires: junit-jupiter, junit-jupiter-params, assertj-core.
javaimport org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.*; class IntegerBoundaryTest { @ParameterizedTest @ValueSource(ints = {Integer.MIN_VALUE, Integer.MIN_VALUE + 1, 0, Integer.MAX_VALUE - 1, Integer.MAX_VALUE}) void shouldHandleIntegerBoundaries(int value) { assertThat(value).isNotNull(); } @Test void shouldDetectIntegerOverflow() { assertThatThrownBy(() -> Math.addExact(Integer.MAX_VALUE, 1)) .isInstanceOf(ArithmeticException.class); } @Test void shouldDetectIntegerUnderflow() { assertThatThrownBy(() -> Math.subtractExact(Integer.MIN_VALUE, 1)) .isInstanceOf(ArithmeticException.class); } @Test void shouldHandleZeroEdge() { int result = MathUtils.divide(0, 5); assertThat(result).isZero(); assertThatThrownBy(() -> MathUtils.divide(5, 0)) .isInstanceOf(ArithmeticException.class); } }
javaimport org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; class StringBoundaryTest { @ParameterizedTest @ValueSource(strings = {"", " ", " ", "\t", "\n"}) void shouldRejectEmptyAndWhitespace(String input) { boolean result = StringUtils.isNotBlank(input); assertThat(result).isFalse(); } @Test void shouldHandleNullString() { String result = StringUtils.trim(null); assertThat(result).isNull(); } @Test void shouldHandleSingleCharacter() { assertThat(StringUtils.capitalize("a")).isEqualTo("A"); assertThat(StringUtils.trim("x")).isEqualTo("x"); } @Test void shouldHandleVeryLongString() { String longString = "x".repeat(1000000); assertThat(longString.length()).isEqualTo(1000000); assertThat(StringUtils.isNotBlank(longString)).isTrue(); } }
javaclass CollectionBoundaryTest { @Test void shouldHandleEmptyList() { List<String> empty = List.of(); assertThat(empty).isEmpty(); assertThat(CollectionUtils.first(empty)).isNull(); assertThat(CollectionUtils.count(empty)).isZero(); } @Test void shouldHandleSingleElementList() { List<String> single = List.of("only"); assertThat(single).hasSize(1); assertThat(CollectionUtils.first(single)).isEqualTo("only"); assertThat(CollectionUtils.last(single)).isEqualTo("only"); } @Test void shouldHandleLargeList() { List<Integer> large = new ArrayList<>(); for (int i = 0; i < 100000; i++) { large.add(i); } assertThat(large).hasSize(100000); assertThat(CollectionUtils.first(large)).isZero(); assertThat(CollectionUtils.last(large)).isEqualTo(99999); } @Test void shouldHandleNullInCollection() { List<String> withNull = new ArrayList<>(List.of("a", null, "c")); assertThat(withNull).contains(null); assertThat(CollectionUtils.filterNonNull(withNull)).hasSize(2); } }
javaclass FloatingPointBoundaryTest { @Test void shouldHandleFloatingPointPrecision() { double result = 0.1 + 0.2; assertThat(result).isCloseTo(0.3, within(0.0001)); } @Test void shouldHandleSpecialFloatingPointValues() { assertThat(Double.POSITIVE_INFINITY).isGreaterThan(Double.MAX_VALUE); assertThat(Double.NEGATIVE_INFINITY).isLessThan(Double.MIN_VALUE); assertThat(Double.NaN).isNotEqualTo(Double.NaN); } @Test void shouldHandleZeroInDivision() { assertThat(1.0 / 0.0).isEqualTo(Double.POSITIVE_INFINITY); assertThat(-1.0 / 0.0).isEqualTo(Double.NEGATIVE_INFINITY); assertThat(0.0 / 0.0).isNaN(); } }
javaclass DateTimeBoundaryTest { @Test void shouldHandleMinAndMaxDates() { LocalDate min = LocalDate.MIN; LocalDate max = LocalDate.MAX; assertThat(min).isBefore(max); assertThat(DateUtils.isValid(min)).isTrue(); assertThat(DateUtils.isValid(max)).isTrue(); } @Test void shouldHandleLeapYearBoundary() { LocalDate leapYearEnd = LocalDate.of(2024, 2, 29); assertThat(leapYearEnd).isNotNull(); } @Test void shouldRejectInvalidDateInNonLeapYear() { assertThatThrownBy(() -> LocalDate.of(2023, 2, 29)) .isInstanceOf(DateTimeException.class); } }
javaclass ArrayBoundaryTest { @Test void shouldHandleFirstElementAccess() { int[] array = {1, 2, 3, 4, 5}; assertThat(array[0]).isEqualTo(1); } @Test void shouldHandleLastElementAccess() { int[] array = {1, 2, 3, 4, 5}; assertThat(array[array.length - 1]).isEqualTo(5); } @Test void shouldThrowOnNegativeIndex() { int[] array = {1, 2, 3}; assertThatThrownBy(() -> array[-1]) .isInstanceOf(ArrayIndexOutOfBoundsException.class); } @Test void shouldThrowOnOutOfBoundsIndex() { int[] array = {1, 2, 3}; assertThatThrownBy(() -> array[10]) .isInstanceOf(ArrayIndexOutOfBoundsException.class); } @Test void shouldHandleEmptyArray() { int[] empty = {}; assertThat(empty.length).isZero(); assertThatThrownBy(() -> empty[0]) .isInstanceOf(ArrayIndexOutOfBoundsException.class); } }
Math.addExact() to detect silent overflowNaN != NaN; use Float.isNaN() or Double.isNaN()| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | pass→pass | 14,329 | 10,333 | -28% | 1 | 1 | 0% | 2,682 | 4,347 | +62% | 0 | 0 | — |
case-01 | pass→pass | 14,874 | 11,484 | -23% | 1 | 1 | 0% | 3,000 | 4,701 | +57% | 0 | 0 | — |
case-02 | pass→pass | 3,622 | 2,976 | -18% | 1 | 1 | 0% | 578 | 2,932 | +407% | 0 | 0 | — |
case-03 | pass→pass | 3,732 | 3,079 | -17% | 1 | 1 | 0% | 662 | 2,841 | +329% | 0 | 0 | — |
case-04 | pass→pass | 14,457 | 9,947 | -31% | 1 | 1 | 0% | 2,818 | 4,390 | +56% | 0 | 0 | — |
case-05 | fail→pass | 12,674 | 9,770 | -23% | 1 | 1 | 0% | 2,248 | 4,259 | +89% | 0 | 0 | — |
case-06 | pass→pass | 11,081 | 7,479 | -33% | 1 | 1 | 0% | 2,078 | 3,696 | +78% | 0 | 0 | — |
case-07 | fail→pass | 17,769 | 14,186 | -20% | 1 | 1 | 0% | 2,918 | 4,645 | +59% | 0 | 0 | — |
case-08 | fail→pass | 9,128 | 6,503 | -29% | 1 | 1 | 0% | 1,707 | 3,595 | +111% | 0 | 0 | — |
case-10 | fail→pass | 14,898 | 9,232 | -38% | 1 | 1 | 0% | 2,436 | 4,057 | +67% | 0 | 0 | — |
case-11 | pass→pass | 14,223 | 8,032 | -44% | 1 | 1 | 0% | 2,476 | 3,787 | +53% | 0 | 0 | — |
case-12 | pass→pass | 8,911 | 5,339 | -40% | 1 | 1 | 0% | 1,637 | 3,366 | +106% | 0 | 0 | — |
case-13 | pass→pass | 10,831 | 9,192 | -15% | 1 | 1 | 0% | 2,197 | 4,049 | +84% | 0 | 0 | — |
case-14 | pass→pass | 9,798 | 7,508 | -23% | 1 | 1 | 0% | 1,898 | 4,001 | +111% | 0 | 0 | — |
case-15 | fail→pass | 15,720 | 14,592 | -7% | 1 | 1 | 0% | 3,055 | 5,233 | +71% | 0 | 0 | — |
case-16 | fail→pass | 12,495 | 6,991 | -44% | 1 | 1 | 0% | 2,491 | 3,839 | +54% | 0 | 0 | — |
case-17 | pass→pass | 10,226 | 6,214 | -39% | 1 | 1 | 0% | 1,954 | 3,606 | +85% | 0 | 0 | — |
case-18 | pass→pass | 4,743 | 8,062 | +70% | 1 | 1 | 0% | 828 | 3,678 | +344% | 0 | 0 | — |
case-19 | pass→pass | 9,979 | 8,441 | -15% | 1 | 1 | 0% | 1,757 | 3,967 | +126% | 0 | 0 | — |
case-20 | pass→pass | 9,281 | 6,479 | -30% | 1 | 1 | 0% | 1,808 | 3,511 | +94% | 0 | 0 | — |
case-21 | pass→pass | 8,669 | 5,888 | -32% | 1 | 1 | 0% | 1,765 | 3,555 | +101% | 0 | 0 | — |
case-22 | pass→pass | 9,167 | 5,631 | -39% | 1 | 1 | 0% | 1,697 | 3,303 | +95% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.