unit-test-utility-methods

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unit Testing Utility Classes and Static Methods

单元测试工具类与静态方法

Overview

概述

This skill provides comprehensive patterns for testing utility classes and static methods using JUnit 5. It covers testing pure functions without side effects, edge case handling, boundary conditions, and common utility patterns like string manipulation, calculations, collections, and data validation.
本技能提供使用JUnit 5测试工具类和静态方法的全面模式,涵盖无副作用纯函数的测试、边界情况处理、边界条件,以及字符串处理、计算、集合、数据验证等常见工具模式。

When to Use

适用场景

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
在以下场景中使用本技能:
  • 测试包含静态辅助方法的工具类
  • 测试无状态、无副作用的纯函数
  • 测试字符串处理与格式化工具
  • 测试计算与转换工具
  • 测试集合与数组工具
  • 希望编写无需复杂Mock的简单、快速测试
  • 测试数据转换与验证辅助方法

Instructions

操作步骤

Follow these steps to test utility classes and static methods:
按照以下步骤测试工具类与静态方法:

1. Create Test Class

1. 创建测试类

Create a JUnit 5 test class named after the utility class being tested (e.g., StringUtilsTest).
创建与待测试工具类同名的JUnit 5测试类(例如:StringUtilsTest)。

2. Test Happy Path

2. 测试正常路径

Write tests for typical use cases with valid inputs to verify correct behavior.
编写针对典型有效输入用例的测试,验证正确行为。

3. Test Edge Cases

3. 测试边界情况

Test null inputs, empty strings, zero values, and boundary conditions.
测试空输入、空字符串、零值及边界条件。

4. Test Error Cases

4. 测试错误场景

Verify proper exception throwing for invalid inputs when applicable.
适当时,验证无效输入下是否正确抛出异常。

5. Use Descriptive Test Names

5. 使用描述性测试名称

Name tests to clearly indicate what scenario is being tested (e.g., shouldCapitalizeFirstLetter).
为测试命名以清晰表明所测试的场景(例如:shouldCapitalizeFirstLetter)。

6. Use AssertJ Assertions

6. 使用AssertJ断言

Leverage AssertJ's readable assertion methods for clear test code.
利用AssertJ可读性强的断言方法编写清晰的测试代码。

7. Consider Parameterized Tests

7. 考虑参数化测试

Use @ParameterizedTest for testing multiple similar scenarios with different inputs.
使用@ParameterizedTest测试多个相似场景的不同输入。

Examples

示例

Basic Pattern: Static Utility Testing

基础模式:静态工具测试

Simple String Utility

简单字符串工具

java
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");
  }
}
java
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

空值安全工具方法

java
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();
  }
}
java
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

数学工具

java
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);
  }
}
java
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

列表/集合/映射操作

java
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");
  }
}
java
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

格式化与解析工具

java
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");
  }
}
java
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

验证器工具

java
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();
  }
}
java
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

使用@ParameterizedTest的多测试用例

java
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);
  }
}
java
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

使用Mockito测试外部依赖

Utility with Dependency (Rare Case)

含依赖的工具类(罕见场景)

java
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));
  }
}
java
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

边界情况与极限测试

java
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));
  }
}
java
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
  • 仅测试纯函数 - 无副作用或状态
  • 覆盖正常路径与边界情况 - 空值、空集合、极值
  • 使用描述性测试名称 - 清晰说明测试内容
  • 保持测试简洁短小 - 工具类测试应易于理解
  • 使用@ParameterizedTest 测试多个相似场景
  • 无需时避免Mock - 仅对外部依赖进行Mock
  • 测试边界条件 - 最大/最小值、空集合、空输入

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
  • 测试框架行为而非工具逻辑
  • 纯函数无需Mock时过度Mock
  • 未测试空值/空边界情况
  • 未测试负数与极值
  • 测试方法过大 - 拆分复杂场景

Constraints and Warnings

约束与注意事项

  • Static methods cannot be mocked: Use reflection-based utilities like PowerMock only when absolutely necessary
  • Pure function requirement: Utility methods should be pure functions; testing stateful utilities is difficult
  • Floating point precision: Never use exact equality for floating point comparisons; use tolerance
  • Null handling consistency: Decide whether utilities return null or throw exceptions for invalid input
  • Immutable inputs: Document clearly whether utility methods modify input parameters
  • Thread safety: Static utilities must be thread-safe; verify under concurrent access
  • External dependencies: Minimize external dependencies in utility classes for easier testing
  • 静态方法无法被Mock:仅在绝对必要时使用PowerMock等基于反射的工具
  • 纯函数要求:工具方法应是纯函数;有状态工具类的测试难度大
  • 浮点精度:浮点比较绝不能使用精确相等,需使用容差
  • 空值处理一致性:明确工具类对无效输入是返回空值还是抛出异常
  • 不可变输入:明确说明工具方法是否修改输入参数
  • 线程安全:静态工具类必须线程安全;需在并发访问下验证
  • 外部依赖:尽量减少工具类的外部依赖以简化测试

Examples

示例

Input: Utility Method Without Tests

输入:无测试的工具方法

java
public class StringUtils {
    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }
}
java
public class StringUtils {
    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }
}

Output: Complete Test Coverage

输出:完整测试覆盖

java
class StringUtilsTest {
    @Test
    void shouldReturnTrueForNullString() {
        assertThat(StringUtils.isEmpty(null)).isTrue();
    }

    @Test
    void shouldReturnTrueForEmptyString() {
        assertThat(StringUtils.isEmpty("")).isTrue();
    }

    @Test
    void shouldReturnTrueForWhitespaceOnly() {
        assertThat(StringUtils.isEmpty("   ")).isTrue();
    }

    @Test
    void shouldReturnFalseForNonEmptyString() {
        assertThat(StringUtils.isEmpty("hello")).isFalse();
    }
}
java
class StringUtilsTest {
    @Test
    void shouldReturnTrueForNullString() {
        assertThat(StringUtils.isEmpty(null)).isTrue();
    }

    @Test
    void shouldReturnTrueForEmptyString() {
        assertThat(StringUtils.isEmpty("")).isTrue();
    }

    @Test
    void shouldReturnTrueForWhitespaceOnly() {
        assertThat(StringUtils.isEmpty("   ")).isTrue();
    }

    @Test
    void shouldReturnFalseForNonEmptyString() {
        assertThat(StringUtils.isEmpty("hello")).isFalse();
    }
}

Input: Calculation Without Edge Case Testing

输入:无边界测试的计算方法

java
public static double percentage(int value, int total) {
    return (value / total) * 100.0;
}
java
public static double percentage(int value, int total) {
    return (value / total) * 100.0;
}

Output: Tests Covering Edge Cases

输出:覆盖边界情况的测试

java
class MathUtilsTest {
    @Test
    void shouldCalculateNormalPercentage() {
        assertThat(MathUtils.percentage(25, 100)).isEqualTo(25.0);
    }

    @Test
    void shouldHandleZeroDivisor() {
        assertThat(MathUtils.percentage(50, 0)).isEqualTo(0.0);
    }

    @Test
    void shouldHandleZeroValue() {
        assertThat(MathUtils.percentage(0, 100)).isEqualTo(0.0);
    }
}
java
class MathUtilsTest {
    @Test
    void shouldCalculateNormalPercentage() {
        assertThat(MathUtils.percentage(25, 100)).isEqualTo(25.0);
    }

    @Test
    void shouldHandleZeroDivisor() {
        assertThat(MathUtils.percentage(50, 0)).isEqualTo(0.0);
    }

    @Test
    void shouldHandleZeroValue() {
        assertThat(MathUtils.percentage(0, 100)).isEqualTo(0.0);
    }
}

Constraints and Warnings

约束与注意事项

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.
浮点精度问题:使用
isCloseTo()
并设置容差,而非精确相等。
空值处理不一致:明确工具类对无效输入是返回空值还是抛出异常,然后保持一致的测试。
复杂工具逻辑应拆分:考虑将复杂逻辑重构为可测试的单元。

References

参考资料