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

Spring Boot JUnit测试:Service层与Mapper类测试触发空指针异常

问题分析与解决方案

问题描述

运行测试代码时,UserServiceImpl中的userMapper为空对象引用,调用其addRequestUserToEntity方法触发空指针异常。

测试类代码

@RunWith(SpringRunner.class)
class UserServiceTest {
    @InjectMocks
    UserMapper userMapper = mock(UserMapper.class);
    @InjectMocks
    UserServiceImpl userServiceImpl=mock(UserServiceImpl.class);
    @Mock
    UserRepository userRepository = mock(UserRepository.class);
    @Mock
    UserService userService=mock(UserService.class);
    @BeforeEach
    public void setup(){
        MockitoAnnotations.initMocks(this);
        userRepository = Mockito.mock(UserRepository.class);
      
        userServiceImpl = new UserServiceImpl(userRepository);
    }


    @Test
    void add() {
        AddUserRequest addUserRequest = new AddUserRequest("guest","1234567890","guest98787@guest.com","1234567890");

        User user = new User();
        user.setUserId(1);
        user.setUid("12345678");
        user.setName("guest");
        user.setMobileNo("1234567890");
        user.setEmail("guest98787@guest.com");
        user.setPassword("1234567890");
        WalletDetail walletDetail=new WalletDetail();
        walletDetail.setWalletId(1);
        walletDetail.setUid("12345");
        walletDetail.setUser(user);
        walletDetail.setWalletAmount(10.0);
        user.setWallet(walletDetail);

        when(userMapper.addRequestUserToEntity(addUserRequest)).thenReturn(user);
        when(userRepository.save(user)).thenReturn(user);
        when(userServiceImpl.add(addUserRequest)).thenReturn(user);
        User result = userServiceImpl.add(addUserRequest);
       
        verify(userServiceImpl,times(1)).add(addUserRequest);
        assertEquals(user.getUserId(), result.getUserId());
    }
}

Service层代码

@Slf4j
@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WalletService walletService;

    private UserServiceImpl userServiceImpl;
    

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository=userRepository;
    }
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public User add(AddUserRequest addUserRequest) {
        log.debug("Request received for user registration:  " + addUserRequest);

        User user = createUser(addUserRequest);

        WalletDetail walletDetail = walletService.createDefaultWallet();
        walletDetail.setUser(user);
        user.setWallet(walletDetail);

        log.debug("User saved with data " + user);
        return userRepository.save(user);
    }

    private User createUser(AddUserRequest addUserRequest) {
        User user = userMapper.addRequestUserToEntity(addUserRequest);
        user.setCreatedBy(user.getUid());
        user.setUpdatedBy(user.getUid());
        return user;
    }
}

错误信息

java.lang.NullPointerException: Cannot invoke "bytro.user.management.transformer.UserMapper.addRequestUserToEntity(bytro.user.management.model.request.AddUserRequest)" because "this.userMapper" is null

    at bytro.user.management.service.impl.UserServiceImpl.createUser(UserServiceImpl.java:64)
    at bytro.user.management.service.impl.UserServiceImpl.add(UserServiceImpl.java:53)
    at bytro.user.management.service.UserServiceTest.add(UserServiceTest.java:124)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

问题原因

  1. 测试类中userMapper错误使用@InjectMocks注解,该注解用于将Mock对象注入被测试对象,而非标记Mock实例本身。
  2. setup方法中手动创建userServiceImpl时,仅传入了userRepository,未注入userMapper和walletService,导致这两个依赖为空。
  3. 初始时将userServiceImpl设为Mock对象,会跳过真实业务逻辑的执行,失去测试意义。

解决方案

修改测试类,正确初始化依赖并注入:

@RunWith(SpringRunner.class)
class UserServiceTest {
    // 用@Mock标记需要模拟的依赖
    @Mock
    private UserMapper userMapper;
    @Mock
    private UserRepository userRepository;
    @Mock
    private WalletService walletService;

    // 用@InjectMocks标记被测试的真实对象,自动注入Mock依赖
    @InjectMocks
    private UserServiceImpl userServiceImpl;

    @BeforeEach
    public void setup(){
        MockitoAnnotations.initMocks(this);
        // 无需手动创建userServiceImpl,@InjectMocks会自动实例化并注入依赖
    }

    @Test
    void add() {
        AddUserRequest addUserRequest = new AddUserRequest("guest","1234567890","guest98787@guest.com","1234567890");

        User user = new User();
        user.setUserId(1);
        user.setUid("12345678");
        user.setName("guest");
        user.setMobileNo("1234567890");
        user.setEmail("guest98787@guest.com");
        user.setPassword("1234567890");
        WalletDetail walletDetail=new WalletDetail();
        walletDetail.setWalletId(1);
        walletDetail.setUid("12345");
        walletDetail.setUser(user);
        walletDetail.setWalletAmount(10.0);
        user.setWallet(walletDetail);

        // 模拟walletService的方法返回值
        when(walletService.createDefaultWallet()).thenReturn(walletDetail);
        when(userMapper.addRequestUserToEntity(addUserRequest)).thenReturn(user);
        when(userRepository.save(user)).thenReturn(user);

        // 调用真实的Service方法
        User result = userServiceImpl.add(addUserRequest);
       
        // 验证依赖的调用次数
        verify(userRepository,times(1)).save(user);
        verify(userMapper,times(1)).addRequestUserToEntity(addUserRequest);
        verify(walletService,times(1)).createDefaultWallet();
        assertEquals(user.getUserId(), result.getUserId());
    }
}

关键修改点

  • 将userMapper、walletService改为@Mock标记,明确这些是需要模拟的依赖。
  • 被测试对象userServiceImpl用@InjectMocks标记,Mockito自动完成实例化和依赖注入。
  • 移除手动创建userServiceImpl的代码,避免覆盖注入后的实例。
  • 补充模拟walletService.createDefaultWallet()的返回值,避免新增空指针。
  • 调整验证逻辑,改为验证真实依赖的调用次数,确保业务流程符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 23:12:25