JUnit测试中TheConfiguration Mock为空引发空指针异常问题排查
问题分析与解决
问题重现
待测试的Authentication类:
@Component @Slf4j public class Authentication { private final RestTemplate restTemplate; @Autowired private TheConfiguration config; public Authentication(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public String generateAuthentification(){ String plainCreds = config.geUser()+":"+config.getPassword(); byte[] plainCredsBytes = plainCreds.getBytes(); byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); return new String(base64CredsBytes); } }
JUnit测试代码:
import static org.mockito.Mockito.when; import org.apache.commons.codec.binary.Base64; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.web.client.RestTemplate; import com.example.TheConfiguration; @ExtendWith(MockitoExtension.class) class AuthenticationTest { @InjectMocks Authentication authentication; @Mock RestTemplate restTemplate; @Mock TheConfiguration config; @Test void testGenerateAuthentifications() { when(config.getUser()).thenReturn("user"); when(config.getPassword()).thenReturn("pass"); String plainCreds = config.getUser() + ":" + config.getPassword(); System.out.println(plainCreds); byte[] plainCredsBytes = plainCreds.getBytes(); byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); String a = new String(base64CredsBytes); assertEquals(authentication.generateAuthentification(),a); } }
运行测试时抛出NullPointerException:
Cannot invoke "com.example.TheConfiguration.getUser()" because "this.config" is null
完整错误日志:
java.lang.NullPointerException: Cannot invoke "com.example.TheConfiguration.getUser()" because "this.config" is null at com.example.Authentication.generateAuthentification(Authentication.java:26) at com.example.AuthenticationTest.testGenerateAuthentifications(AuthenticationTest.java:38) ... 省略后续堆栈信息
错误原因
Mockito的@InjectMocks注入优先级为:构造函数注入 > setter方法注入 > 字段注入。你的Authentication类有一个带参数的构造函数(仅注入RestTemplate),Mockito会优先使用这个构造函数创建实例,不会自动注入@Autowired标注的config字段,导致config为null。
另外,代码里存在笔误:config.geUser()应该是config.getUser(),这也是后续需要修正的点。
解决方法
方法1:构造函数注入所有依赖(推荐,符合Spring最佳实践)
将TheConfiguration加入构造函数,移除字段上的@Autowired:
@Component @Slf4j public class Authentication { private final RestTemplate restTemplate; private final TheConfiguration config; // Spring 4.3+支持构造函数自动装配,无需@Autowired public Authentication(RestTemplate restTemplate, TheConfiguration config) { this.restTemplate = restTemplate; this.config = config; } public String generateAuthentification(){ // 修正笔误:geUser -> getUser String plainCreds = config.getUser()+":"+config.getPassword(); byte[] plainCredsBytes = plainCreds.getBytes(); byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); return new String(base64CredsBytes); } }
修改后,Mockito的@InjectMocks会自动通过构造函数注入两个Mock对象,测试代码无需改动。
方法2:给config添加Setter方法
如果不想修改构造函数,可以给config添加Setter方法,让Mockito通过Setter注入:
@Component @Slf4j public class Authentication { private final RestTemplate restTemplate; private TheConfiguration config; public Authentication(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @Autowired public void setConfig(TheConfiguration config) { this.config = config; } public String generateAuthentification(){ String plainCreds = config.getUser()+":"+config.getPassword(); // 修正笔误 byte[] plainCredsBytes = plainCreds.getBytes(); byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); return new String(base64CredsBytes); } }
方法3:测试中手动注入config
在测试方法里通过反射手动给authentication的config字段赋值:
@Test void testGenerateAuthentifications() { // 手动注入config ReflectionTestUtils.setField(authentication, "config", config); when(config.getUser()).thenReturn("user"); when(config.getPassword()).thenReturn("pass"); String plainCreds = config.getUser() + ":" + config.getPassword(); byte[] plainCredsBytes = plainCreds.getBytes(); byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); String a = new String(base64CredsBytes); assertEquals(authentication.generateAuthentification(),a); }
需要导入org.springframework.test.util.ReflectionTestUtils类。
内容的提问来源于stack exchange,提问作者Marcos
相关产品推荐
相关产品推荐

