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

JUnit测试中Mockito模拟对象为null的问题求助

Mockito单元测试返回Null的问题排查与解决

问题场景

使用JUnit 5 + Mockito编写UserService的单元测试时,所有测试均返回null,断言失败。以下是相关代码及错误信息:

测试类代码

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @InjectMocks
    private UserService userService;
    @Mock
    private UserDAO userDAO;
    @Mock
    private ModelMapper modelMapper;
    @Mock
    private UserDTOMapper userDTOMapper;

    private User user;
    private UserDTO userDTO;

    private AutoCloseable closeable;

    @BeforeTestMethod
    public void initMocks(){
        MockitoAnnotations.openMocks(this);
    }

    @BeforeEach
    void setUp() {
        userDTO = UserDTO.builder()
                .username("johnny")
                .firstname("John")
                .surname("Rogers")
                .email("john@gmail.com")
                .phoneNumber("0123456789")
                .role(Role.ADMIN).build();
        user = User.builder()
                .username("johnny")
                .password("johnny123")
                .firstname("John")
                .surname("Rogers")
                .email("john@gmail.com")
                .phoneNumber("0123456789")
                .role(Role.ADMIN).build();
    }

    @Test
    void findUser() {
        userDao.sace(user);
        assertEquals(userDTO, userService.findUser(user.getUsername()));
    }

    @Test
    void convertToDto() {
        assertEquals(userDTO, userDTOMapper.apply(user));
    }


    @AfterTestMethod
    public void releaseMocks() throws Exception {
        closeable.close();
    }
}

服务类代码

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserDAO userDAO;
    private final ModelMapper modelMapper;
    private final UserDTOMapper userDTOMapper;

    public UserDTO findUser(String username) {
        User user = userDAO.findUserByUsername(username);
        return userDTOMapper.apply(user);
    }

    public UserDTO convertToDto(User user) {
        return modelMapper.map(user, UserDTO.class);
    }
}

错误信息

org.opentest4j.AssertionFailedError: 
Expected :UserDTO(id=0, username=johnny, firstname=John, surname=Rogers, email=john@gmail.com, phoneNumber=0123456789, role=USER)
Actual   :null
<Click to see difference>

    at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
    at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
    at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
    at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
    at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
    at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1142)

问题分析与修正

核心问题点

  1. Mock初始化冗余且错误:已经使用@ExtendWith(MockitoExtension.class),无需手动调用MockitoAnnotations.openMocks(this),且closeable未赋值会导致资源释放报错,同时干扰Mock对象的正确注入。
  2. 未Stub Mock方法:Mockito的Mock对象默认返回null,测试中未对依赖的DAO、Mapper方法设置返回值,导致服务方法调用后返回null。
  3. 测试逻辑错误:findUser测试中调用userDao.sace(user)是拼写错误(应为save),且该调用对服务方法的逻辑无意义,服务类的findUser依赖的是findUserByUsername方法。
  4. JUnit注解混用:@BeforeTestMethod和@AfterTestMethod是JUnit 4的注解,JUnit 5应使用@BeforeEach和@AfterEach。

修正后的测试类代码

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @InjectMocks
    private UserService userService;
    @Mock
    private UserDAO userDAO;
    @Mock
    private ModelMapper modelMapper;
    @Mock
    private UserDTOMapper userDTOMapper;

    private User user;
    private UserDTO userDTO;

    @BeforeEach
    void setUp() {
        userDTO = UserDTO.builder()
                .username("johnny")
                .firstname("John")
                .surname("Rogers")
                .email("john@gmail.com")
                .phoneNumber("0123456789")
                .role(Role.ADMIN).build();
        user = User.builder()
                .username("johnny")
                .password("johnny123")
                .firstname("John")
                .surname("Rogers")
                .email("john@gmail.com")
                .phoneNumber("0123456789")
                .role(Role.ADMIN).build();
    }

    @Test
    void findUser() {
        // Stub DAO方法,指定调用时的返回值
        when(userDAO.findUserByUsername(user.getUsername())).thenReturn(user);
        // Stub Mapper方法,指定转换后的返回值
        when(userDTOMapper.apply(user)).thenReturn(userDTO);
        
        UserDTO result = userService.findUser(user.getUsername());
        assertEquals(userDTO, result);
    }

    @Test
    void convertToDto() {
        // Stub ModelMapper的转换方法
        when(modelMapper.map(user, UserDTO.class)).thenReturn(userDTO);
        
        UserDTO result = userService.convertToDto(user);
        assertEquals(userDTO, result);
    }
}

修正说明

  • 移除冗余的Mock初始化和资源释放代码,由MockitoExtension自动处理Mock的生命周期。
  • 为每个测试中依赖的Mock方法添加when().thenReturn()Stub,确保调用时返回预设的对象,而非默认的null。
  • 修正测试逻辑,使其与服务类的实际调用链一致,确保测试覆盖真实的业务流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:23:28