Spring Boot中JUnit5测试依赖注入失败的原因及替代注入方案
场景说明
基于Spring Boot、JUnit5和Lombok的项目中,编写了一个简单的工具类:
public class Calculator { public int subtract(int a, int b) { return a - b; } }
对应的测试类代码如下:
import lombok.AllArgsConstructor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @AllArgsConstructor class CalculatorTest { private final Calculator calculator; private static final int SUBTRACTION_RESULT = 2; @Test void subtractTest() { int result = calculator.subtract(5, 3); assertEquals(result, SUBTRACTION_RESULT); } }
报错信息
运行测试后抛出异常:
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [final com.udemy.junits.Calculator calculator] in constructor [public com.udemy.junits.CalculatorTest(com.udemy.junits.Calculator)].
已尝试操作
- 移除Lombok的
@AllArgsConstructor,手动编写构造器,依然报相同错误:
public CalculatorTest(Calculator calculator) { this.calculator = calculator; }
- 使用
@BeforeAll手动实例化Calculator,测试正常运行:
@BeforeAll static void init() { calculator = new Calculator(); }
疑问
- 是否因为未启动应用上下文导致无法依赖注入?
- 能否不通过
new Calculator()的方式实现依赖注入?
解答
依赖注入失败的核心原因
没错,你的判断完全正确——JUnit5本身不具备Spring的依赖注入能力,默认情况下不会启动Spring应用上下文。JUnit5只会直接实例化测试类,当测试类构造器需要Calculator参数时,它找不到对应的参数解析器(ParameterResolver)来提供实例,因此抛出异常。
无需手动new的依赖注入方案
有几种可行的方案可以实现依赖注入,不用手动创建实例:
1. 启动完整Spring上下文(最简方案)
给测试类添加@SpringBootTest注解,它会启动完整的Spring应用上下文。同时要确保Calculator被Spring管理(添加@Component注解):
修改Calculator类:
import org.springframework.stereotype.Component; @Component public class Calculator { public int subtract(int a, int b) { return a - b; } }
修改测试类:
import lombok.AllArgsConstructor; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest @AllArgsConstructor class CalculatorTest { private final Calculator calculator; private static final int SUBTRACTION_RESULT = 2; @Test void subtractTest() { int result = calculator.subtract(5, 3); assertEquals(SUBTRACTION_RESULT, result); } }
2. 启动轻量上下文(按需加载Bean)
如果不想启动完整应用上下文,可以用@ExtendWith(SpringExtension.class)配合@ContextConfiguration,只加载测试需要的Bean配置:
import lombok.AllArgsConstructor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = CalculatorTest.TestConfig.class) @AllArgsConstructor class CalculatorTest { private final Calculator calculator; private static final int SUBTRACTION_RESULT = 2; @Configuration static class TestConfig { @Bean public Calculator calculator() { return new Calculator(); } } @Test void subtractTest() { int result = calculator.subtract(5, 3); assertEquals(SUBTRACTION_RESULT, result); } }
3. 自定义JUnit5参数解析器
如果只是单纯不想在测试代码里写new,可以自定义一个ParameterResolver来提供Calculator实例,但这种方式本质还是手动创建实例,仅适合有复杂实例化逻辑的场景,一般不推荐用于简单工具类测试。
额外提示
对于Calculator这类无依赖的简单工具类,手动new实例是最轻便的测试方式,无需启动Spring上下文,测试执行速度更快。只有当测试需要依赖Spring容器中的其他Bean(比如Service依赖Repository)时,才需要启动上下文进行依赖注入。
内容的提问来源于stack exchange,提问作者Sam Fisher

