【问题标题】:springboot jpa repository.save(obj) must not be null, NullPointerException in testspringboot jpa repository.save(obj) 不能为null,测试中出现NullPointerException
【发布时间】:2022-01-03 02:13:14
【问题描述】:

我在为我的 Kotlin 项目编写单元测试时遇到问题,我的实际使用情况很好。但是,我的单元测试会抛出一个不寻常的错误。当我在测试中调用我的保存服务时,它会抛出一个java.lang.NullPointerException: movieRepository.save(movie) must not be null。我不确定“不得为空”指的是什么项目,因为我同时记录了 movieRepository 和 movie 以具有一定的价值。

附加信息:只有当我添加一个新条目时,它才会抛出 NPE 错误。但是,当我执行更新(也使用.save(movie))时,测试可以正常进行。使用 .findAll() 和 .deleteById() 等其他功能进行测试可以正常工作。

这里是sn-ps的代码:

电影.kt

@Table(name="movie")
@Entity
data class Movie(
  @Id
  @Column(name="id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  val id: Int? = null,

  val name: String,

  val type: String,
)

电影服务.kt

@Service
class MovieService {
  @Autowired
  private lateinit var movieRepository: MovieRepository;

  fun addMovie(movie: Movie): Movie {
    return movieRepository.save(movie)
  }
}

MovieServiceTest.kt

@ExtendWith(MockitoExtension::class)
@MockitoSettings(strictness = Strictness.LENIENT)
internal class MovieServiceTest {

  @Mock
  private lateinit var mockMovieRepository: MovieRepository;

  @InjectMocks
  private lateinit var movieServiceTest: MovieService;

  // Mock Values
  val mockName = "mockName";
  val mockType = "mockType";
  val mockMovie = Movie(name = mockName, type = mockType);

  @Test
  fun addMovie() {
    val result = movieServiceTest.addMovie(mockMovie);

    val movieArgumentCaptor: ArgumentCaptor<Movie> = ArgumentCaptor.forClass(Movie::class.java);
    verify(mockMovieRepository).save(movieArgumentCaptor.capture());

    val capturedMovie = movieArgumentCaptor.value;
    verify(mockMovieRepository).save(capturedMovie);

    verify(result.name == mockName)
  }
}

错误信息

java.lang.NullPointerException: movieRepository.save(movie) must not be null

    at com.example.stackoverflow.MovieService.addMovie(MovieService.kt:18)
    at com.example.stackoverflow.MovieServiceTest.addMovie(MovieServiceTest.kt:43)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
    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$7(TestMethodTestDescriptor.java:214)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
    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:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    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:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)


Process finished with exit code 255

【问题讨论】:

    标签: spring-boot kotlin spring-data-jpa junit5 spring-boot-test


    【解决方案1】:

    您的测试中有 2 个错误。

    第一个错误:您必须模拟您的 repository.save 方法。 Kotlin 说 "java.lang.NullPointerException: movieRepository.save(movie) must not be null" 因为不模拟 movieRepository.save 方法将返回 null。不幸的是,您的 MovieService.addMovie 不会返回可为空的。因此出现了 nullPointerException。 您可以这样模拟存储库调用:

    Mockito.`when`(mockMovieRepository.save(mockMovie)).thenReturn(mockMovie)
    

    第二个错误:在你的最后一个断言verify(result.name == mockName) 我猜你想断言 result.name 等于 mockName。你可以为此目的使用 junit Assertions (https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions)

    Mockito.verify 旨在验证发生的某些行为,例如调用一次或多次的方法。

    总结一下:

        @Test
        fun addMovie() {
            Mockito.`when`(mockMovieRepository.save(mockMovie)).thenReturn(mockMovie)
            val result = movieServiceTest.addMovie(mockMovie);
            
            val movieArgumentCaptor: ArgumentCaptor<Movie> = ArgumentCaptor.forClass(Movie::class.java);
    
            verify(mockMovieRepository).save(movieArgumentCaptor.capture());
    
            val capturedMovie = movieArgumentCaptor.value;
            verify(mockMovieRepository).save(capturedMovie);
            assertEquals(result.name, mockName)
        }
    

    【讨论】:

    • 感谢您的回答。意识到我的错误,因为我一直在做given(mockMovieRepository.findAll()).willReturn(someValue),但不是为了 .save()。一个奇怪的问题,在 given().willReturn 和 when().thenReturn 之间是否有首选方式?或者它只是一样
    • 在我看来,这些只是不同的风格。第一个是普通的 Mockito 语法,第二个只是试图更好地适应 BDD(行为驱动开发)样式测试 - 我真的很喜欢第二个版本,因为它在 BDD 测试中读起来非常好。
    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    相关资源
    最近更新 更多