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

SpringBoot含if语句的待测试类服务测试失败问题排查

问题

我正在为SpringBoot应用编写测试,待测试类UserServiceImpl的getUserById方法返回Optional<User>,该方法通过if语句判断仓库是否返回数据,若未查询到则抛出ResourceNotFoundException。保留if语句时测试始终触发该异常,移除if语句后测试通过,请问我遗漏了什么?

待测试类UserServiceImpl

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    @Override
    public List<User> getUsers() {
        log.info("Fetching users");
        return userRepository.findAll();
    }

    @Override
    public Optional<User> getUserById(Long id) {
        log.info("Fetching user id: {}", id);
        Optional<User> user = userRepository.findById(id);
        if (!user.isPresent()) {
            throw new ResourceNotFoundException(MessageUtil.ERROR_USER_NOTFOUND);
        }
        return user;
    }
}

测试类UserServiceImplTest

@RunWith(SpringRunner.class)
@SpringBootTest
class UserServiceImplTest {

    @MockBean
    private UserRepository userRepositoryTest;

    @InjectMocks
    private UserServiceImpl userServiceTest;

    @Mock
    private PasswordEncoder passwordEncoder;

    private List<User> userSet;
    private User user1;
    private User user2;

    @BeforeEach
    void setUp() {
        userServiceTest = new UserServiceImpl(userRepositoryTest, passwordEncoder);

        Set<ApplicationUserRole> roles = new HashSet<>();
        roles.add(ApplicationUserRole.TEST_USER);
        userSet = new ArrayList<>();

        user1 = User.builder().nickname("test-nickname")
                .id(1L)
                .username("254701234567")
                .roles(roles)
                .password("password")
                .build();

        user2 = User.builder().nickname("test2-nickname2")
                .id(2L)
                .username("254701234589")
                .roles(roles)
                .password("password")
                .build();

        userSet.add(user1);
        userSet.add(user2);

        userSet.stream().forEach(user -> {
            userServiceTest.saveUser(user);
        });
    }

    @AfterEach
    void tearDown() {
    }

    @Test
    void testGetUsers() {
        when(userServiceTest.getUsers()).thenReturn(userSet);
        assertEquals(2, userServiceTest.getUsers().size());
        verify(userRepositoryTest).findAll();
    }

    @Test
    void testGetUserById() {
        when(userServiceTest.getUserById(user1.getId())).thenReturn(Optional.ofNullable(user1));
        assertEquals(1, user1.getId());
        verify(userRepositoryTest).findById(user1.getId());
    }

    @Test
    void testSaveUser() {
        when(userServiceTest.saveUser(user1)).thenReturn(user1);
        assertEquals(1L, user1.getId());
        verify(userRepositoryTest).save(user1);
    }

    @Test
    void updateUser() {
        user1.setNickname("nickname-update");
        when(userServiceTest.saveUser(user1)).thenReturn(user1);
        assertEquals("nickname-update", user1.getNickname());
        verify(userRepositoryTest).save(user1);
    }

}

说明:其他测试均正常运行


问题原因及解决方案

核心问题

你搞反了mock的对象——现在是直接mock服务类的方法,而非mock底层的Repository方法。

比如testGetUserById里的这段代码:

when(userServiceTest.getUserById(user1.getId())).thenReturn(Optional.ofNullable(user1));

这相当于直接给服务方法硬塞返回值,但实际运行时,服务方法内部还是会调用userRepository.findById(id),而你没给这个Repository方法打桩,它默认返回Optional.empty(),自然触发了if语句里的异常。

移除if语句后,服务方法直接返回Repository的结果,但你又mock了服务方法的返回值,所以测试能通过,但这等于跳过了服务类里的逻辑校验,完全没测到你要验证的异常抛出逻辑。

修正方案

1. 修正testGetUserById测试

改成mock Repository的findById方法,再调用服务方法验证逻辑:

@Test
void testGetUserById() {
    // 给Repository的findById打桩,返回预期用户
    when(userRepositoryTest.findById(user1.getId())).thenReturn(Optional.of(user1));
    
    // 调用服务方法获取结果
    Optional<User> result = userServiceTest.getUserById(user1.getId());
    
    // 验证结果正确性
    assertEquals(user1.getId(), result.get().getId());
    // 验证Repository方法确实被调用
    verify(userRepositoryTest).findById(user1.getId());
}

2. 补充异常场景测试

还应该加一个测试,验证查询不存在的ID时确实会抛出异常:

@Test
void testGetUserById_NotFound() {
    Long nonExistentId = 999L;
    // 打桩:查询不存在的ID返回空
    when(userRepositoryTest.findById(nonExistentId)).thenReturn(Optional.empty());
    
    // 验证抛出指定异常
    assertThrows(ResourceNotFoundException.class, () -> {
        userServiceTest.getUserById(nonExistentId);
    });
    
    verify(userRepositoryTest).findById(nonExistentId);
}

3. 修正其他测试方法

你的其他测试(比如testGetUsers、testSaveUser)也存在同样的问题,都是在mock服务方法而非Repository方法,需要一并修正。比如testGetUsers应该改成:

@Test
void testGetUsers() {
    // mock Repository的findAll方法
    when(userRepositoryTest.findAll()).thenReturn(userSet);
    // 调用服务方法
    List<User> result = userServiceTest.getUsers();
    // 验证结果
    assertEquals(2, result.size());
    verify(userRepositoryTest).findAll();
}

内容的提问来源于stack exchange,提问作者Steve Otieno

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:15:40