【问题标题】:Java Spring Reactive Rest API Test Class Gives Error - Null Pointer ExceptionJava Spring Reactive Rest API 测试类给出错误 - 空指针异常
【发布时间】:2019-03-19 20:08:38
【问题描述】:

我正在尝试使用 Spring Reactive 编程创建一个非常基本的 Rest API,并且我还想尝试使用 Reactive Stack 进行测试。

但是,当我尝试测试服务实现时,我得到一个空指针错误,这发生在我调用任何类型的操作方法后,例如:doNext、flatMap、block 等(以 userService.save(user) 的形式.doNext、userService.save(user).flatMap、userService.save(user).block() 等)

您可以在以下 github 存储库中找到完整的代码和 gradle 设置: https://github.com/mesarikaya/restAPI

您的帮助将不胜感激。

如果您想快速浏览一下,下面是测试代码:

@Slf4j
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;
    private User existingUser;
    private User newUser;
    private User returnUser;
    private Logger logger = LoggerFactory.getLogger(UserServiceTest.class);
    @InjectMocks
    private UserServiceImpl userService;

    @BeforeEach
    public void setUp() {
        System.out.println("@Beforeeach is called!");
        MockitoAnnotations.initMocks(this);

        // Create Existing Account
        existingUser = new User();
        existingUser.setEmail("abc@gmail.com");
        existingUser.setFirstName("First");
        existingUser.setMiddleName("Middle");
        existingUser.setLastName("Last");
        existingUser.setActive(true);
        existingUser.setMobileNumber("021234234");
        existingUser.setVerified(false);
        existingUser.setPermalink("abcgmailcom");

        Address address1 = new Address();
        address1.setStreetName("asda");
        address1.setHouseNumber("asd");
        address1.setCity("asdasd");
        address1.setCountry("asdasd");
        address1.setZipcode("sasd123");
        existingUser.setAddress(address1);

        // New User
        // Create Existing Account
        newUser = new User();
        newUser.setFirstName("NewFirst");
        newUser.setMiddleName("NewMiddle");
        newUser.setLastName("NewLast");
        newUser.setActive(true);
        newUser.setMobileNumber("0123123");
        newUser.setVerified(false);
        newUser.setOauthId("123123123");

        Address address2 = new Address();
        address2.setStreetName("ADress2");
        address2.setHouseNumber("asda");
        address2.setCity("asdsad");
        address2.setCountry("asd");
        address2.setZipcode("asdasd");
        newUser.setAddress(address2);
        // newUser.setId((ObjectId) new ObjectIdGenerator().generate());
        userService.saveOrUpdateUser(existingUser);
        userService.saveOrUpdateUser(newUser);

        System.out.println("Existing user: " + existingUser);
        System.out.println("New User: " + newUser);
    }

    @Test
    public void findUser(){
        User user = userService.saveOrUpdateUser(existingUser).block();
        Mono<User> userMono = userService.saveOrUpdateUser(existingUser);

        StepVerifier
                .create(userMono)
                .assertNext(u -> {
                    assertEquals(existingUser.getEmail(), u.getEmail());
                    assertEquals(existingUser.getLastName() , u.getLastName());
                })
                .expectComplete()
                .verify();

        assertEquals(user.getEmail(), existingUser.getEmail());
        assertEquals(user.getId(), existingUser.getId());
    }
}

这是错误信息:

仅供参考 - 存储库已被识别:com.mes.gotogether.repositories.UserRepository$MockitoMock$612061122

java.lang.NullPointerException 在 com.mes.gotogether.services.UserServiceImpl.saveOrUpdateUser(UserServiceImpl.java:56) 在 com.mes.gotogether.services.UserServiceTest.setUp(UserServiceTest.java:77) 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:566) 在 org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532) 在 org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115) 在 org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeMethodInExtensionContext(ClassTestDescriptor.java:436) 在 org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$14(ClassTestDescriptor.java:424) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$2(TestMethodTestDescriptor.java:136) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:156) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:135) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:110) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) 在 java.base/java.util.ArrayList.forEach(ArrayList.java:1540) 在 org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) 在 java.base/java.util.ArrayList.forEach(ArrayList.java:1540) 在 org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) 在 org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) 在 org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) 在 org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) 在 org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) 在 org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) 在 org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) 在 org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) 在 org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) 在 com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74) 在 com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) 在 com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) 在 com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

【问题讨论】:

    标签: java spring testing junit reactive-programming


    【解决方案1】:

    您的服务从您的存储库中获取 Mono&lt;User&gt; 并在 Mono 上调用 block()(这本身就是一个坏主意,但这是另一回事)。

    您的测试将一个模拟存储库注入到服务中,但是当调用其findByUserId 方法时,您从未告诉模拟返回任何内容。所以它返回null。在 null 上调用 block() 会引发 NullPointerException。

    【讨论】:

    • 您好 JB,首先,感谢您的帮助和时间。我觉得我在概念上遗漏了某事并且没有得到你的意思。 findByUserId 方法返回一个 Mono 然后我调用 block() 方法。我怎么能解决这个问题?你能举个例子吗?如果我从生成示例用户的引导类进行测试,则相同的代码可以正常工作。
    • 您是在问为什么以及如何避免调用 block(),或者为什么以及如何避免此 NullPointerException?
    • 再次感谢。我认为在这两个方面接受教育会非常有帮助。我提出这个问题的第一个目标是了解如何解决空指针异常并使单元测试正常工作。如果您还可以让我知道比阻止更好的选择,我将不胜感激。
    • 好吧,我已经解释了为什么你在我的回答中得到 NPE。你不明白什么?你了解mock的原理吗?
    • 嗨,JB,您能否分享一个可以解决问题的示例代码。如前所述,我真的不明白你的解释。我认为这是因为我对 mocking 和单元测试还没有太多的了解。我正在尝试构建它。在我看来,如果我运行正常代码并且服务可以轻松使用它并返回一个用户对象,存储库就已经可以正常工作了。只有在测试模式下,我才会收到这个错误,我需要一个具体的例子来说明如何对其进行更改以使其工作。如果您能提供基本的示例代码解释,我将不胜感激。
    【解决方案2】:

    对于那些不太熟悉 Mocks 和 Stubs 并且想了解为什么在我的案例中会发生 NullPointer 异常的人,这里有一篇很好的文章来解释这个案例。 https://reflectoring.io/unit-testing-spring-boot/

    这是我遇到的问题的解决方案: - 测试方法适用于以下:

     @Test
     public void saveUser(){
    
        when(userRepository.findByUserId(anyString())).thenReturn(Mono.just(existingUser));
        when(userRepository.save(any(User.class))).thenReturn(Mono.just(existingUser));
        User retrievedUser = userServiceImpl.saveOrUpdateUser(existingUser).log().flux().next().block();
        System.out.println("Returned user: " + retrievedUser.getEmail());
        System.out.println("Existing user" + existingUser.toString());
        assertEquals(retrievedUser.getUserId(), existingUser.getUserId());
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-27
      • 2018-07-05
      • 1970-01-01
      相关资源
      最近更新 更多