You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 22:47:14