不使用@InjectMocks时单元测试依赖注入空指针问题排查
问题根源
你直接通过new PageModelBuilder()实例化被测类,但@Inject注解是依赖注入框架(如Spring、CDI)或Mockito的@InjectMocks才会处理的标记,JVM手动实例化对象时只会初始化成员变量的默认值(引用类型为null),不会自动注入stringTranslator,所以才会出现空指针异常。
解决方法(无需修改被测类)
方法一:使用Mockito的@InjectMocks(推荐)
Mockito的@InjectMocks会自动将你标记为@Mock的依赖注入到被测类实例中,无需手动实例化和设置。修改测试类如下:
@RunWith(MockitoJUnitRunner.Silent.class) public class PageModelBuilderTest { @Mock private StringTranslator stringTranslator; @InjectMocks // 自动注入mock依赖 private PageModelBuilder pageModelBuilder; @Before public void setup() { if (!AppConfig.isInitialized()) { AppConfig.initialize("WebApp", null, new String[] { "--root=.", "--domain=test"}); } } @After public void after() { if (AppConfig.isInitialized()) { AppConfig.destroy(); } } @Test public void givenProductDataWithProductLink_whenBuildPageModel_thenPageModelHasCanonicalURL() { AppConfig.insertString("baseUrl", "https://www.google.com"); when(stringTranslator.getStringById("page_description_format")).thenReturn("sample description"); ProductData productData = ProductTestData.getProductDataBuilder() .title("TestMed") .productLink("/PRODUCT-DESCRIPTION/dp/asin") .build(); PageModel pageModel = pageModelBuilder.buildFrom(productData); assertEquals("https://www.google.com/PRODUCT-DESCRIPTION/dp/asin", pageModel.getCanonicalURL()); // 可选:验证mock对象的调用逻辑 verify(stringTranslator).getStringById("page_description_format"); } }
方法二:反射手动注入(适合无法使用@InjectMocks的场景)
如果必须手动实例化被测类,可以通过Java反射绕过访问权限,将mock的stringTranslator设置到私有成员变量中:
在测试方法中实例化pageModelBuilder后添加以下代码:
pageModelBuilder = new PageModelBuilder(); // 反射获取私有成员并注入mock Field translatorField = PageModelBuilder.class.getDeclaredField("stringTranslator"); translatorField.setAccessible(true); // 开启私有字段访问权限 translatorField.set(pageModelBuilder, stringTranslator); // 注入mock对象
注意事项
- 方法一基于Mockito原生机制,代码简洁易维护,是单元测试中依赖注入的标准做法。
- 方法二依赖反射,代码繁琐且容易因类结构变更失效,仅作为备选方案。
内容的提问来源于stack exchange,提问作者Ajit Goel
相关产品推荐
相关产品推荐

