JUnit5单元测试断言异常

wen java案例 2

本文目录导读:

JUnit5单元测试断言异常

  1. assertThrows - 断言抛出指定异常
  2. assertDoesNotThrow - 断言不抛出异常
  3. 多异常类型断言
  4. 异常链测试
  5. 测试自定义异常
  6. 使用 Lambda 表达式的高级用法
  7. 最佳实践

在JUnit 5中,测试异常(Assertions for Exceptions)主要通过 org.junit.jupiter.api.Assertions 类中的 assertThrowsassertDoesNotThrow 方法来实现。

assertThrows - 断言抛出指定异常

assertThrows 是最常用的异常断言方法,它会验证被测试代码是否抛出了指定类型的异常。

基本用法

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ExceptionTest {
    @Test
    void testThrowException() {
        // assertThrows(异常类型, 可执行代码)
        assertThrows(IllegalArgumentException.class, () -> {
            // 这里应该抛出IllegalArgumentException的代码
            throw new IllegalArgumentException("参数错误");
        });
    }
}

获取异常对象进行进一步断言

assertThrows 会返回捕获到的异常对象,可以用来进行更具体的验证:

@Test
void testExceptionMessage() {
    // 获取异常对象
    IllegalArgumentException exception = assertThrows(
        IllegalArgumentException.class,
        () -> {
            throw new IllegalArgumentException("参数不能为负数");
        }
    );
    // 验证异常消息
    assertEquals("参数不能为负数", exception.getMessage());
    // 验证异常的cause
    assertNull(exception.getCause());
}

实际业务场景示例

public class Calculator {
    public int divide(int a, int b) {
        if (b == 0) {
            throw new ArithmeticException("除数不能为0");
        }
        return a / b;
    }
    public void validateAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄必须在0-150之间");
        }
    }
    public String getUserName(int userId) {
        if (userId <= 0) {
            throw new IllegalArgumentException("用户ID必须大于0");
        }
        // 模拟数据库查询
        return "用户" + userId;
    }
}
// 测试类
public class CalculatorTest {
    private final Calculator calculator = new Calculator();
    @Test
    void testDivideByZero() {
        ArithmeticException exception = assertThrows(
            ArithmeticException.class,
            () -> calculator.divide(10, 0)
        );
        assertEquals("除数不能为0", exception.getMessage());
    }
    @Test
    void testValidateAgeInvalid() {
        assertThrows(IllegalArgumentException.class, 
            () -> calculator.validateAge(-1));
        assertThrows(IllegalArgumentException.class, 
            () -> calculator.validateAge(151));
    }
    @Test
    void testGetUserNameWithNegativeId() {
        Exception exception = assertThrows(
            IllegalArgumentException.class,
            () -> calculator.getUserName(-5)
        );
        assertTrue(exception.getMessage().contains("用户ID"));
    }
}

assertDoesNotThrow - 断言不抛出异常

用于验证代码执行过程中没有抛出任何异常:

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@Test
void testNoException() {
    assertDoesNotThrow(() -> {
        // 这段代码不应该抛出异常
        int result = 10 / 2;
    });
}
@Test
void testSuccessfulOperation() {
    assertDoesNotThrow(() -> {
        calculator.validateAge(25);
    });
}

多异常类型断言

@Test
void testMultipleExceptions() {
    // 验证不同的输入导致不同的异常
    assertThrows(IllegalArgumentException.class, 
        () -> calculator.validateAge(-1));
    assertThrows(IllegalArgumentException.class, 
        () -> calculator.validateAge(151));
    // 正常情况不抛出异常
    assertDoesNotThrow(() -> calculator.validateAge(25));
}

异常链测试

@Test
void testExceptionChain() {
    Exception exception = assertThrows(RuntimeException.class, () -> {
        try {
            throw new IOException("IO错误");
        } catch (IOException e) {
            throw new RuntimeException("运行时错误", e);
        }
    });
    assertEquals("运行时错误", exception.getMessage());
    assertNotNull(exception.getCause());
    assertTrue(exception.getCause() instanceof IOException);
}

测试自定义异常

// 自定义异常
class BusinessException extends RuntimeException {
    private final int errorCode;
    public BusinessException(int errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    public int getErrorCode() {
        return errorCode;
    }
}
// 测试
@Test
void testCustomException() {
    BusinessException exception = assertThrows(
        BusinessException.class,
        () -> {
            throw new BusinessException(1001, "业务异常");
        }
    );
    assertEquals(1001, exception.getErrorCode());
    assertEquals("业务异常", exception.getMessage());
}

使用 Lambda 表达式的高级用法

@Test
void testExceptionWithConditions() {
    // 使用lambda表达式进行更复杂的验证
    assertThrows(IllegalArgumentException.class, () -> {
        // 复杂的业务逻辑
        String input = "invalid";
        if (input.length() > 5) {
            throw new IllegalArgumentException("输入过长");
        }
    });
}

最佳实践

public class ExceptionTestBestPractices {
    // 1. 明确测试目的
    @Test
    void shouldThrowExceptionWhenInputIsNegative() {
        assertThrows(IllegalArgumentException.class, 
            () -> new Calculator().validateAge(-1));
    }
    // 2. 使用有意义的测试方法名
    @Test
    void divideByZero_ShouldThrowArithmeticException() {
        assertThrows(ArithmeticException.class, 
            () -> new Calculator().divide(10, 0));
    }
    // 3. 避免过度使用assertThrows
    @Test
    void shouldNotThrowExceptionForValidInput() {
        assertDoesNotThrow(() -> {
            new Calculator().validateAge(30);
        });
    }
    // 4. 测试边界值
    @Test
    void boundaryValueTesting() {
        // 测试年龄边界值
        assertThrows(IllegalArgumentException.class, 
            () -> new Calculator().validateAge(-1));
        assertThrows(IllegalArgumentException.class, 
            () -> new Calculator().validateAge(151));
        assertDoesNotThrow(() -> new Calculator().validateAge(0));
        assertDoesNotThrow(() -> new Calculator().validateAge(150));
    }
}
  • assertThrows:验证代码抛出指定异常,可获取异常对象进行进一步检查
  • assertDoesNotThrow:验证代码不抛出任何异常
  • 异常链测试:可以测试异常的cause和层级关系
  • 最佳实践:测试方法名要清晰,测试边界值,避免过度测试

抱歉,评论功能暂时关闭!