JUnit5测试继承类时@InjectMocks注入对象为null的问题
问题原因
你猜的没错,Mockito的@InjectMocks在处理带有显式构造方法的类时,会优先尝试构造方法注入而非字段注入。但你的MyCustomHandler构造方法里并没有Object1和Object2的参数,导致这两个@Autowired的私有字段完全没被注入,自然会出现null。
解决办法
办法一:修改构造方法,支持构造注入
这是最符合依赖注入规范的方案,把Object1和Object2加入构造参数,让Mockito能通过构造方法自动注入依赖:
public class MyCustomHandler extends SomeHandler { private final Object1 object1; private final Object2 object2; // 加入Object1、Object2参数,保留父类所需参数 @Autowired public MyCustomHandler (String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order, Object1 object1, Object2 object2) { super(name, servicesManager, principalFactory, order); this.object1 = object1; this.object2 = object2; } public boolean doSomething () { return object1.foo(); } // 其他重写方法 }
测试类无需修改,@InjectMocks会自动把@Mock注解的object1、object2以及父类构造所需的依赖(需要额外添加@Mock)传入构造方法。
办法二:强制Mockito使用字段注入
如果不想修改原类代码,可以用两种方式强制Mockito绕过构造方法,直接注入私有字段:
方式1:用Spring的ReflectionTestUtils手动注入
@ExtendWith(MockitoExtension.class) class CustomAzureAuthenticationHandlerTest { @Mock Object1 object1 ; @Mock Object2 object2 ; @Mock ServicesManager servicesManager; @Mock PrincipalFactory principalFactory; MyCustomHandler myCustomHandler; @BeforeEach void setUp() { // 手动创建实例,传入父类需要的mock参数 myCustomHandler = new MyCustomHandler("test-handler", servicesManager, principalFactory, 1); // 反射注入私有字段 ReflectionTestUtils.setField(myCustomHandler, "object1", object1); ReflectionTestUtils.setField(myCustomHandler, "object2", object2); } @Test void doSomeTest() { when(object1.foo()).thenReturn(false); boolean result = myCustomHandler.doSomething(); // 这里添加断言逻辑,比如assertThat(result).isFalse(); } }
方式2:指定@InjectMocks的注入模式(Mockito 4.6+支持)
@ExtendWith(MockitoExtension.class) class CustomAzureAuthenticationHandlerTest { @Mock Object1 object1 ; @Mock Object2 object2 ; @Mock ServicesManager servicesManager; @Mock PrincipalFactory principalFactory; // 指定优先字段注入 @InjectMocks @Setter(InjectMocksSelectionMode.FIELD) MyCustomHandler myCustomHandler = new MyCustomHandler("test-handler", servicesManager, principalFactory, 1); @Test void doSomeTest() { when(object1.foo()).thenReturn(false); myCustomHandler.doSomething(); } }
办法三:完全手动创建实例并注入
简单直接,完全绕开@InjectMocks,手动控制所有依赖:
@ExtendWith(MockitoExtension.class) class CustomAzureAuthenticationHandlerTest { @Mock Object1 object1 ; @Mock Object2 object2 ; MyCustomHandler myCustomHandler; @BeforeEach void setUp() { // 手动mock父类所需的参数 ServicesManager mockServices = mock(ServicesManager.class); PrincipalFactory mockPrincipal = mock(PrincipalFactory.class); myCustomHandler = new MyCustomHandler("test", mockServices, mockPrincipal, 1); // 反射注入私有字段 try { Field field1 = MyCustomHandler.class.getDeclaredField("object1"); field1.setAccessible(true); field1.set(myCustomHandler, object1); Field field2 = MyCustomHandler.class.getDeclaredField("object2"); field2.setAccessible(true); field2.set(myCustomHandler, object2); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } @Test void doSomeTest() { when(object1.foo()).thenReturn(false); myCustomHandler.doSomething(); } }
方案选择建议
- 优先选办法一,构造注入是Spring推荐的依赖注入方式,代码更清晰,测试也更简单。
- 若无法修改原类,选办法二的方式1,
ReflectionTestUtils是Spring测试里的常用工具,稳定性高。
内容的提问来源于stack exchange,提问作者Micho Rizo
相关产品推荐
相关产品推荐

