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

Spring项目中如何Mock Principal对象进行订单创建方法测试?

Spring在线书店订单创建测试问题:Mock Principal对象失败导致报错

我正在学习Spring,开发一个在线书店项目,包含图书下单功能。测试订单创建方法时,该方法通过Principal对象识别请求发起者,不知道如何Mock Principal对象完成测试,目前测试报错。

错误日志

17:52:42.111 [main] ERROR com.example.BookStoreProject.service.OrderServiceImpl -- No value present

java.lang.RuntimeException: Order creation exception

at com.example.BookStoreProject.service.OrderServiceImpl.createOrder(OrderServiceImpl.java:45)
at com.example.BookStoreProject.service.OrderServiceImplTest.canCreateOrder(OrderServiceImplTest.java:67)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

测试类代码

@ExtendWith(MockitoExtension.class)
class OrderServiceImplTest {

    @Mock
    private OrdersRepository ordersRepository;
    @Captor
    private ArgumentCaptor<ArrayList<OrderCreationDtoRequest>> captor;
    @Mock
    private UserService userService;
    @Mock
    private AuthenticationService authenticationService;
    @Mock
    private OrderDetailService orderDetailService;
    @Mock
    private Principal principal;
    private OrderServiceImpl undertest;
    @InjectMocks
    private Orders order = new Orders();
    @BeforeEach
    void setUp() {
        undertest = new OrderServiceImpl(ordersRepository,userService,orderDetailService);
    }

 @Test
    void canCreateOrder(){
        List<OrderCreationDtoRequest> requests = new ArrayList<>();
        order.setAddress("abc");
        order.setCreatedAt(LocalDateTime.now());
        undertest.createOrder(requests,principal);
        ArgumentCaptor<Orders> ordersArgumentCaptor = ArgumentCaptor.forClass(Orders.class);
        verify(orderDetailService).create(captor.capture(),ordersArgumentCaptor.capture());
        Orders capturedOrder = ordersArgumentCaptor.getValue();
        List<OrderCreationDtoRequest> capturedRequest = captor.getValue();
        assertThat(capturedOrder).isEqualTo(order);
        assertThat(capturedRequest).isEqualTo(capturedRequest);
    }
}

待测试类代码

public OrderCreationDtoResponse createOrder(List<OrderCreationDtoRequest> requests, Principal principal) {
        Orders order = new Orders();
        try {
            LocalDateTime createdAt = LocalDateTime.now();
            String email = principal.getName();
            Users user = userService.getByUserEmail(email).orElseThrow();
            String address = requests.get(0).getAddress();
            order.setCreatedAt(createdAt);
            order.setUser(user);
            order.setAddress(address);
            orderDetailService.create(requests,order);
        }catch (Exception e){
            log.error(e.getMessage());
            throw new RuntimeException("Order creation exception");
        }
        return OrderCreationDtoResponse.builder()
                .totalPrice(orderDetailService.totalPrice(requests))
                .build();

    }

问题分析与修复步骤

1. 未Mock Principal的getName()方法

已Mock Principal对象,但未指定principal.getName()的返回值,调用时返回null,导致userService.getByUserEmail(null)返回空Optional,触发orElseThrow()抛出NoSuchElementException(日志中的"No value present"即为此异常消息)。

修复:在测试方法中添加Mock逻辑:

when(principal.getName()).thenReturn("test@example.com");

2. 未Mock UserService的返回值

即使Principal返回邮箱,userService.getByUserEmail(email)默认返回空Optional,同样会触发orElseThrow()。需要Mock该方法返回有效Users对象:

Users testUser = new Users();
// 可按需设置用户属性,如ID等
when(userService.getByUserEmail("test@example.com")).thenReturn(Optional.of(testUser));

3. 请求列表为空导致索引越界

测试中requests是空ArrayList,待测试方法调用requests.get(0).getAddress()会抛出IndexOutOfBoundsException。需构造至少一个请求对象加入列表:

OrderCreationDtoRequest requestDto = new OrderCreationDtoRequest();
requestDto.setAddress("abc");
requests.add(requestDto);

4. @InjectMocks误用

给Orders order添加@InjectMocks属于误用,该对象并非测试类依赖的服务,待测试方法会自行创建Orders对象,应移除该注解。

5. 断言逻辑无效

最后一行assertThat(capturedRequest).isEqualTo(capturedRequest)永远为true,无测试意义,应改为断言捕获的请求与传入的requests相等:

assertThat(capturedRequest).isEqualTo(requests);

修复后的完整测试方法

@Test
void canCreateOrder(){
    // 构造测试数据
    OrderCreationDtoRequest requestDto = new OrderCreationDtoRequest();
    requestDto.setAddress("abc");
    List<OrderCreationDtoRequest> requests = new ArrayList<>();
    requests.add(requestDto);

    // Mock依赖方法
    String testEmail = "test@example.com";
    when(principal.getName()).thenReturn(testEmail);
    
    Users testUser = new Users();
    when(userService.getByUserEmail(testEmail)).thenReturn(Optional.of(testUser));

    // 调用待测试方法
    undertest.createOrder(requests,principal);

    // 验证与断言
    ArgumentCaptor<Orders> ordersArgumentCaptor = ArgumentCaptor.forClass(Orders.class);
    verify(orderDetailService).create(captor.capture(),ordersArgumentCaptor.capture());
    
    Orders capturedOrder = ordersArgumentCaptor.getValue();
    List<OrderCreationDtoRequest> capturedRequest = captor.getValue();
    
    assertThat(capturedOrder.getAddress()).isEqualTo("abc");
    assertThat(capturedOrder.getUser()).isEqualTo(testUser);
    assertThat(capturedRequest).isEqualTo(requests);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:15:03