【问题标题】:Why Mockito says that my stubbings are unnecessary为什么 Mockito 说我的存根是不必要的
【发布时间】:2020-07-22 15:04:58
【问题描述】:

我正在尝试模拟 Spring 的 ConversionService,因此 convert() 方法总是返回我创建的 dto,但 Mockito 说我的存根是不必要的,并且模拟的 ConversionService 返回 null。

代码如下:

StudentServiceTests

package com.mdv.secondservice;

import com.mdv.secondservice.api.dto.ThirdServiceStudentDto;
import com.mdv.secondservice.api.proxy.ThirdServiceProxy;
import com.mdv.secondservice.db.model.Student;
import com.mdv.secondservice.db.repository.StudentRepository;
import com.mdv.secondservice.service.StudentService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.convert.ConversionService;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;

@SpringBootTest
@ExtendWith(MockitoExtension.class)
public class StudentServiceTests {
    @Mock
    private StudentRepository studentRepository;

    @Mock
    private ConversionService conversionService;

    @Mock
    private ThirdServiceProxy thirdServiceProxy;

    @InjectMocks
    private StudentService studentService;

    private Student student;
    private ThirdServiceStudentDto thirdServiceStudentDto;

    @BeforeEach
    void init() {
        final String middleName = "middleName";
        final String lastName = "lastName";
        student = Student.builder()
                .middleName(middleName)
                .lastName(lastName)
                .build();
        thirdServiceStudentDto = new ThirdServiceStudentDto(lastName);
    }

    @Test
    void createStudent_StudentWasCreated() {
        when(conversionService.convert(any(Student.class), same(ThirdServiceStudentDto.class)))
                .thenReturn(thirdServiceStudentDto);
        when(thirdServiceProxy.createStudent(any(ThirdServiceStudentDto.class))).thenReturn(student);
        when(studentService.createStudent(student)).thenReturn(student);

        Student returnedStudent = studentService.createStudent(student);

        assertThat(returnedStudent).isEqualTo(student);
    }
}

学生服务

package com.mdv.secondservice.service;

import com.mdv.secondservice.api.dto.ThirdServiceStudentDto;
import com.mdv.secondservice.api.proxy.ThirdServiceProxy;
import com.mdv.secondservice.db.model.Student;
import com.mdv.secondservice.db.repository.StudentRepository;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class StudentService {
    private final StudentRepository studentRepository;
    private final ConversionService conversionService;
    private final ThirdServiceProxy thirdServiceProxy;

    public Student createStudent(Student student) throws FeignException.InternalServerError {
        ThirdServiceStudentDto thirdServiceStudentDto =
                conversionService.convert(student, ThirdServiceStudentDto.class);
        Student createdStudent = thirdServiceProxy.createStudent(thirdServiceStudentDto);

        createdStudent.setMiddleName(student.getMiddleName());
        studentRepository.save(createdStudent);

        return createdStudent;
    }
}

好像我在第一个 when 调用中搞砸了匹配器。我知道我没有模拟 StudentRepository,我稍后会修复它。问题是 conversionService.convert(...)StudentService.createStudent 方法中调用时返回 null。

UPD(添加了 Mockito 错误):


java.lang.NullPointerException
    at com.mdv.secondservice.service.StudentService.createStudent(StudentService.java:26)
    at com.mdv.secondservice.StudentServiceTests.createStudent_StudentWasCreated(StudentServiceTests.java:59)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
    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:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    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.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
    Suppressed: org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at com.mdv.secondservice.StudentServiceTests.createStudent_StudentWasCreated(StudentServiceTests.java:56)
  2. -> at com.mdv.secondservice.StudentServiceTests.createStudent_StudentWasCreated(StudentServiceTests.java:58)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
        at org.mockito.junit.jupiter.MockitoExtension.afterEach(MockitoExtension.java:181)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAfterEachCallbacks$11(TestMethodTestDescriptor.java:255)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$12(TestMethodTestDescriptor.java:271)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$13(TestMethodTestDescriptor.java:271)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAllAfterMethodsOrCallbacks(TestMethodTestDescriptor.java:270)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAfterEachCallbacks(TestMethodTestDescriptor.java:254)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:143)
        ... 41 more

【问题讨论】:

  • Mockito 的错误信息好吗?
  • 它是否适用于 .convert(any(), any())) ? (仅用于定位问题)以及为什么您尝试模拟此:`when(studentService.createStudent(student)).thenReturn(student);?这是您要测试的服务还是?
  • @rogerdpack 刚刚添加。
  • @pL4Gu33 不,它没有。得到相同的 NRE 指向 createdStudent.setMiddleName(student.getMiddleName()) (StudentService.create)。是的,你是对的,那个模拟是不必要的。但这不是问题,Mockito 指的是上面的另外两个模拟。似乎 Mockito 忽略了这两个..

标签: java spring-boot unit-testing mockito


【解决方案1】:

当你模拟一切时,你不需要 spring boot 上下文。 您可以删除@SpringBootTest。 您的示例应该运行。

否则您可以将@ExtendWith(MockitoExtension.class) 替换为@ExtendWith(SpringExtension.class)

【讨论】:

  • 你是对的,谢谢。我想问题是 Spring 上下文包含没有被嘲笑的 ConversionService。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多