unit-test-utility-methods
Install this skill
npx skills add giuseppe-trisciuoglio/developer-kitWorks across Claude Code, Cursor, Codex, Copilot & Antigravity
This skill facilitates the systematic validation of static utility classes and pure helper functions using the JUnit 5 framework and AssertJ assertion library. Unlike complex business logic that requires dependency injection or stateful mocking, these utility methods are typically stateless and deterministic. This approach centers on verifying inputs against expected outputs, specifically targeting edge cases such as null pointers, empty collections, zero-division, and boundary conditions. By isolating these methods into dedicated test suites, you ensure that basic data transformations, string formatting, and mathematical conversions function reliably across your project. The workflow prioritizes execution speed and maintainability by focusing on direct, predictable functional testing rather than integrated environment setup.
When to Use This Skill
- •Testing custom libraries providing reusable string helpers
- •Validating business-specific math utility methods
- •Ensuring data sanitization routines handle unexpected inputs gracefully
- •Testing collection utility classes that abstract standard stream operations
How to Invoke This Skill
Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:
- “Write unit tests for my static utility class
- “How do I test my StringUtils methods with JUnit 5?
- “Create test cases for these collection helper functions
- “Show me how to test math logic without mocking
- “Generate assertions for this helper method
Pro Tips
- 💡Always aim for 100% test coverage on utility classes, as their pure nature makes them ideal candidates for thorough testing without complex mocks.
- 💡Prioritize testing null inputs, empty strings, zero/negative numbers, and maximum/minimum values to cover common edge cases effectively.
- 💡Combine JUnit 5 with AssertJ for highly readable and expressive assertions, making your tests clear and easy to understand.
What this skill does
- •Validating null-safety and defensive programming checks
- •Testing stateless arithmetic and unit conversion logic
- •Verifying string manipulation, formatting, and sluggification
- •Checking collection processing like filtering, joining, and deduplication
- •Confirming deterministic behavior for data transformation functions
When not to use it
- ✕When the logic involves complex external dependencies or database calls
- ✕When methods rely heavily on shared state or mutable singleton objects
Example workflow
- Identify a standalone utility class in your project
- Create a corresponding test class using JUnit 5
- Define test methods for nominal, boundary, and null input cases
- Apply AssertJ fluent assertions to verify output accuracy
- Execute the test suite to ensure consistent behavior
Prerequisites
- –JUnit 5 dependency
- –AssertJ library for fluent assertions
Pitfalls & limitations
- !Testing stateful methods that rely on external variables
- !Over-testing simple getters that offer no logical validation
- !Failing to account for null inputs in methods that should be null-safe
FAQ
How it compares
Unlike manual testing which is error-prone and ignores edge cases, this automated approach provides a repeatable regression suite that explicitly checks for nulls, empty inputs, and boundary errors.
📄 Full skill instructions — original source: giuseppe-trisciuoglio/developer-kit
Test static utility methods using JUnit 5. Focus on pure functions without side effects, edge cases, and boundary conditions.
## When to Use This Skill
Use this skill when:
- Testing utility classes with static helper methods
- Testing pure functions with no state or side effects
- Testing string manipulation and formatting utilities
- Testing calculation and conversion utilities
- Testing collections and array utilities
- Want simple, fast tests without mocking complexity
- Testing data transformation and validation helpers
## Basic Pattern: Static Utility Testing
### Simple String Utility
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
class StringUtilsTest {
@Test
void shouldCapitalizeFirstLetter() {
String result = StringUtils.capitalize("hello");
assertThat(result).isEqualTo("Hello");
}
@Test
void shouldHandleEmptyString() {
String result = StringUtils.capitalize("");
assertThat(result).isEmpty();
}
@Test
void shouldHandleNullInput() {
String result = StringUtils.capitalize(null);
assertThat(result).isNull();
}
@Test
void shouldHandleSingleCharacter() {
String result = StringUtils.capitalize("a");
assertThat(result).isEqualTo("A");
}
@Test
void shouldNotChangePascalCase() {
String result = StringUtils.capitalize("Hello");
assertThat(result).isEqualTo("Hello");
}
}## Testing Null Handling
### Null-Safe Utility Methods
class NullSafeUtilsTest {
@Test
void shouldReturnDefaultValueWhenNull() {
Object result = NullSafeUtils.getOrDefault(null, "default");
assertThat(result).isEqualTo("default");
}
@Test
void shouldReturnValueWhenNotNull() {
Object result = NullSafeUtils.getOrDefault("value", "default");
assertThat(result).isEqualTo("value");
}
@Test
void shouldReturnFalseWhenStringIsNull() {
boolean result = NullSafeUtils.isNotBlank(null);
assertThat(result).isFalse();
}
@Test
void shouldReturnTrueWhenStringHasContent() {
boolean result = NullSafeUtils.isNotBlank(" text ");
assertThat(result).isTrue();
}
}## Testing Calculations and Conversions
### Math Utilities
class MathUtilsTest {
@Test
void shouldCalculatePercentage() {
double result = MathUtils.percentage(25, 100);
assertThat(result).isEqualTo(25.0);
}
@Test
void shouldHandleZeroDivisor() {
double result = MathUtils.percentage(50, 0);
assertThat(result).isZero();
}
@Test
void shouldRoundToTwoDecimalPlaces() {
double result = MathUtils.round(3.14159, 2);
assertThat(result).isEqualTo(3.14);
}
@Test
void shouldHandleNegativeNumbers() {
int result = MathUtils.absoluteValue(-42);
assertThat(result).isEqualTo(42);
}
}## Testing Collection Utilities
### List/Set/Map Operations
class CollectionUtilsTest {
@Test
void shouldFilterList() {
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
List<Integer> evenNumbers = CollectionUtils.filter(numbers, n -> n % 2 == 0);
assertThat(evenNumbers).containsExactly(2, 4);
}
@Test
void shouldReturnEmptyListWhenNoMatches() {
List<Integer> numbers = List.of(1, 3, 5);
List<Integer> evenNumbers = CollectionUtils.filter(numbers, n -> n % 2 == 0);
assertThat(evenNumbers).isEmpty();
}
@Test
void shouldHandleNullList() {
List<Integer> result = CollectionUtils.filter(null, n -> true);
assertThat(result).isEmpty();
}
@Test
void shouldJoinStringsWithSeparator() {
String result = CollectionUtils.join(List.of("a", "b", "c"), "-");
assertThat(result).isEqualTo("a-b-c");
}
@Test
void shouldHandleEmptyList() {
String result = CollectionUtils.join(List.of(), "-");
assertThat(result).isEmpty();
}
@Test
void shouldDeduplicateList() {
List<String> input = List.of("apple", "banana", "apple", "cherry", "banana");
Set<String> unique = CollectionUtils.deduplicate(input);
assertThat(unique).containsExactlyInAnyOrder("apple", "banana", "cherry");
}
}## Testing String Transformations
### Format and Parse Utilities
class FormatUtilsTest {
@Test
void shouldFormatCurrencyWithSymbol() {
String result = FormatUtils.formatCurrency(1234.56);
assertThat(result).isEqualTo("$1,234.56");
}
@Test
void shouldHandleNegativeCurrency() {
String result = FormatUtils.formatCurrency(-100.00);
assertThat(result).isEqualTo("-$100.00");
}
@Test
void shouldParsePhoneNumber() {
String result = FormatUtils.parsePhoneNumber("5551234567");
assertThat(result).isEqualTo("(555) 123-4567");
}
@Test
void shouldFormatDate() {
LocalDate date = LocalDate.of(2024, 1, 15);
String result = FormatUtils.formatDate(date, "yyyy-MM-dd");
assertThat(result).isEqualTo("2024-01-15");
}
@Test
void shouldSluggifyString() {
String result = FormatUtils.sluggify("Hello World! 123");
assertThat(result).isEqualTo("hello-world-123");
}
}## Testing Data Validation
### Validator Utilities
class ValidatorUtilsTest {
@Test
void shouldValidateEmailFormat() {
boolean valid = ValidatorUtils.isValidEmail("[email protected]");
assertThat(valid).isTrue();
boolean invalid = ValidatorUtils.isValidEmail("invalid-email");
assertThat(invalid).isFalse();
}
@Test
void shouldValidatePhoneNumber() {
boolean valid = ValidatorUtils.isValidPhone("555-123-4567");
assertThat(valid).isTrue();
boolean invalid = ValidatorUtils.isValidPhone("12345");
assertThat(invalid).isFalse();
}
@Test
void shouldValidateUrlFormat() {
boolean valid = ValidatorUtils.isValidUrl("https://example.com");
assertThat(valid).isTrue();
boolean invalid = ValidatorUtils.isValidUrl("not a url");
assertThat(invalid).isFalse();
}
@Test
void shouldValidateCreditCardNumber() {
boolean valid = ValidatorUtils.isValidCreditCard("4532015112830366");
assertThat(valid).isTrue();
boolean invalid = ValidatorUtils.isValidCreditCard("1234567890123456");
assertThat(invalid).isFalse();
}
}## Testing Parameterized Scenarios
### Multiple Test Cases with @ParameterizedTest
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.CsvSource;
class StringUtilsParametrizedTest {
@ParameterizedTest
@ValueSource(strings = {"", " ", "null", "undefined"})
void shouldConsiderFalsyValuesAsEmpty(String input) {
boolean result = StringUtils.isEmpty(input);
assertThat(result).isTrue();
}
@ParameterizedTest
@CsvSource({
"hello,HELLO",
"world,WORLD",
"javaScript,JAVASCRIPT",
"123ABC,123ABC"
})
void shouldConvertToUpperCase(String input, String expected) {
String result = StringUtils.toUpperCase(input);
assertThat(result).isEqualTo(expected);
}
}## Testing with Mockito for External Dependencies
### Utility with Dependency (Rare Case)
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class DateUtilsTest {
@Mock
private Clock clock;
@Test
void shouldGetCurrentDateFromClock() {
Instant fixedTime = Instant.parse("2024-01-15T10:30:00Z");
when(clock.instant()).thenReturn(fixedTime);
LocalDate result = DateUtils.today(clock);
assertThat(result).isEqualTo(LocalDate.of(2024, 1, 15));
}
}## Edge Cases and Boundary Testing
class MathUtilsEdgeCaseTest {
@Test
void shouldHandleMaxIntegerValue() {
int result = MathUtils.increment(Integer.MAX_VALUE);
assertThat(result).isEqualTo(Integer.MAX_VALUE);
}
@Test
void shouldHandleMinIntegerValue() {
int result = MathUtils.decrement(Integer.MIN_VALUE);
assertThat(result).isEqualTo(Integer.MIN_VALUE);
}
@Test
void shouldHandleVeryLargeNumbers() {
BigDecimal result = MathUtils.add(
new BigDecimal("999999999999.99"),
new BigDecimal("0.01")
);
assertThat(result).isEqualTo(new BigDecimal("1000000000000.00"));
}
@Test
void shouldHandleFloatingPointPrecision() {
double result = MathUtils.multiply(0.1, 0.2);
assertThat(result).isCloseTo(0.02, within(0.0001));
}
}## Best Practices
- **Test pure functions exclusively** - no side effects or state
- **Cover happy path and edge cases** - null, empty, extreme values
- **Use descriptive test names** - clearly state what's being tested
- **Keep tests simple and short** - utility tests should be quick to understand
- **Use @ParameterizedTest** for testing multiple similar scenarios
- **Avoid mocking when not needed** - only mock external dependencies
- **Test boundary conditions** - min/max values, empty collections, null inputs
## Common Pitfalls
- Testing framework behavior instead of utility logic
- Over-mocking when pure functions need no mocks
- Not testing null/empty edge cases
- Not testing negative numbers and extreme values
- Test methods too large - split complex scenarios
## Troubleshooting
**Floating point precision issues**: Use
isCloseTo() with delta instead of exact equality.**Null handling inconsistency**: Decide whether utility returns null or throws exception, then test consistently.
**Complex utility logic belongs elsewhere**: Consider refactoring into testable units.
## References
- [JUnit 5 Parameterized Tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests)
- [AssertJ Assertions](https://assertj.github.io/assertj-core-features-highlight.html)
- [Testing Edge Cases and Boundaries](https://www.baeldung.com/testing-properties-methods-using-mockito)
How to Use This Skill Unit
Option A: Project-Specific (Recommended)
- Click "Download" above
- In your project, create the directory:
.agent/skills/unit-test-utility-methods/ - Save the file as
SKILL.md - The agent will automatically discover the skill based on its description.
Option B: Global Installation (All Agents)
Save the file to these locations to make it available across all projects:
- Claude Code:
~/.claude/skills/giuseppe-trisciuoglio/developer-kit/unit-test-utility-methods/SKILL.md - Cursor:
~/.cursor/skills/giuseppe-trisciuoglio/developer-kit/unit-test-utility-methods/SKILL.md - Antigravity:
~/.gemini/antigravity/skills/giuseppe-trisciuoglio/developer-kit/unit-test-utility-methods/SKILL.md
🚀 Install with CLI:npx skills add giuseppe-trisciuoglio/developer-kit