Claude Code Plugins

Community-maintained marketplace

Feedback

unit-test-utility-methods

@giuseppe-trisciuoglio/developer-kit
8
0

Unit tests for utility/helper classes and static methods. Test pure functions and helper logic. Use when validating utility code correctness.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name unit-test-utility-methods
description Unit tests for utility/helper classes and static methods. Test pure functions and helper logic. Use when validating utility code correctness.
category testing
tags junit-5, unit-testing, utility, static-methods, pure-functions
version 1.0.1

Unit Testing Utility Classes and Static Methods

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("user@example.com");
    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