Spring REST Controller单元测试:创建账户时Account始终为null的问题
为啥你的测试里Account总是返回null?我来给你捋清楚
嘿,你的问题其实是Mockito mock对象的典型用法误区,咱们一步步拆解:
核心原因1:Mock的AccountService没设置返回值
你在测试里用mock(AccountService.class)创建了一个模拟的服务对象,但没告诉它当调用createAccount(account)时该返回啥。Mockito的默认规则是:所有mock的方法如果没配置返回值,都会返回null(引用类型)。所以当控制器执行account = accountService.createAccount(account)这行时,拿到的是null,自然最终返回的ResponseEntity里的body就是null了。
核心原因2:Mock的UriComponentsBuilder没处理链式调用
控制器里用到了uriBuilder.path(...).buildAndExpand(...).toUri()这种链式调用,你直接mock了UriComponentsBuilder,但没配置path()方法的返回值——它默认会返回null,后续调用buildAndExpand的时候直接就会抛出空指针异常,这也会导致你的测试逻辑走不下去,甚至可能间接让返回的account变成null。
修复后的测试代码
我给你调整了测试代码,把这两个问题都解决了:
@Test public void givenValidAccount_whenCreateAccount_thenSuccessed() throws Exception { // Arrange // 1. 配置AccountService的mock返回值 AccountService accountService = mock(AccountService.class); Account mockSavedAccount = new Account("someone@domain.com"); mockSavedAccount.setId(1L); // 给返回的Account设置一个有效的ID // 告诉mock:不管传入什么Account对象,createAccount都返回我们预设的mockSavedAccount when(accountService.createAccount(any(Account.class))).thenReturn(mockSavedAccount); // 2. 配置UriComponentsBuilder的链式调用mock UriComponentsBuilder uriBuilder = mock(UriComponentsBuilder.class); UriComponents uriComponents = mock(UriComponents.class); // 链式调用的每一步都要配置返回值,保证调用链不中断 when(uriBuilder.path(anyString())).thenReturn(uriBuilder); when(uriBuilder.buildAndExpand(anyLong())).thenReturn(uriComponents); when(uriComponents.toUri()).thenReturn(new URI("/accounts/1")); AccountController accountController = new AccountController(accountService); Account account = new Account("someone@domain.com"); // Act ResponseEntity<?> createdAccount = accountController.createAccount(account, "", uriBuilder); // Assert assertNotNull(createdAccount.getBody()); assertEquals(HttpStatus.CREATED, createdAccount.getStatusCode()); assertEquals(mockSavedAccount, createdAccount.getBody()); }
额外的小建议
- 你控制器里的
authorizationHeader参数目前没用到,要么在测试里传个合理的值(比如"Bearer test-token"),要么干脆从控制器方法里删掉它,避免不必要的参数干扰。 - 控制器里判断邮箱非空的逻辑
!account.getEmail().equalsIgnoreCase("")可以换成Spring的StringUtils.hasText(account.getEmail()),这样能同时处理空字符串和空白字符串的情况,更严谨。 - 可以用
@Mock注解配合@ExtendWith(MockitoExtension.class)来简化mock对象的创建,让测试代码更清爽。
内容的提问来源于stack exchange,提问作者Ebeid ElSayed
相关产品推荐
相关产品推荐

