【问题标题】:NullPointerException on controller when testing in SpringBoot API - Mocking在 SpringBoot API 中测试时控制器上出现 NullPointerException - 模拟
【发布时间】:2022-01-12 07:28:42
【问题描述】:

我不知道为什么,但是当我尝试使用 MVC 从控制器测试我的任何方法时,我的代码给了我一个 nullPointerException 错误。这里我放了部分代码:

我的控制器(仅限第一种方法):

@RestController
@RequestMapping(path = "/companias")
public class ControllerAPI {


    @Autowired
    private final CompaniaServiceImpl companiaService;

    public ControllerAPI(CompaniaServiceImpl companiaService) {
        this.companiaService = companiaService;
    }

    @ResponseStatus(HttpStatus.OK)
    @GetMapping("/{companiaId}/{field_name}")
    public Map<String, List<Object>> getByField(
        @PathVariable("companiaId") Long companiaId,
        @PathVariable("field_name") String fieldName) {

        return companiaService.getCompaniaByfield(companiaId, fieldName);

    }

这是我的服务(我试图模拟的那个):

@Service
@Slf4j
public class CompaniaServiceImpl implements CompaniaService {


    private final CompaniaRepository companiaRepository;
    private final DefaultGroupRepository defaultGroupRepository;
    private final OfficeRepository officeRepository;

    @Autowired
    public CompaniaServiceImpl(CompaniaRepository companiaRepository, DefaultGroupRepository defaultGroupRepository, OfficeRepository officeRepository) {
        this.companiaRepository = companiaRepository;
        this.defaultGroupRepository = defaultGroupRepository;
        this.officeRepository = officeRepository;
    }


    public Map<String, List<Object>> getCompaniaByfield(Long companiaId, String fieldName) {

        if (companiaRepository.existsById(companiaId)) {
            Map<String, Function<Compania, Object>> mapCompania = Map.of(
                "name", Compania::getName,
                "dominio", Compania::getDominio,
                "altas", Compania::getAltas,
                "bajas", Compania::getBajas
            );

            Compania c = companiaRepository.findCompaniaById(companiaId);
            Function<Compania, Object> retriever = mapCompania.get(fieldName);


            return Collections.singletonMap("success", List.of(retriever.apply(c)));
        }
        else throw new IllegalStateException(
            "Compania con id (" + companiaId + ") no existe"
        );

    }

这是我的控制器测试

@ExtendWith(SpringExtension.class)
@ContextConfiguration
class ControllerAPITest {

    String token = "xxx";

    @InjectMocks
    private ControllerAPI controllerAPI;

    private CompaniaServiceImpl companiaService = Mockito.mock(CompaniaServiceImpl.class);

    private MockMvc mockMvc;

    @Before
    public void setup() throws Exception{

        MockitoAnnotations.initMocks(this);

        this.mockMvc = MockMvcBuilders.standaloneSetup(controllerAPI).build();
    }


    @Test
    void getByField() throws Exception {
        Map<String, List<Object>> response = Collections.singletonMap("success", List.of("nombre1"));
        Mockito.when(companiaService.getCompaniaByfield(1L,"name")).thenReturn(response);

        this.mockMvc.perform(
                get("/companias/{companiaId}/{field_name}", 1L, "name")
                    .header("authorization", "Bearer " + token)
                    .contentType(MediaType.APPLICATION_JSON))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.success[0]").isNotEmpty())
            .andExpect(jsonPath("$.success[0]").value("nombre1"));
    }

以及给我的 NullPointerException 错误

java.lang.NullPointerException
    at database.configuration.controller.ControllerAPITest.getByField(ControllerAPITest.java:69)
    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:1541)
    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:1541)
    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: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)

它在 this.mockMvc.perform( 我不知道为什么我无法模拟我的服务的行中给我这个错误。如果您能帮助我,我将不胜感激

【问题讨论】:

  • 1.使用接口,你定义了那些,所以使用它们(在你的控制器和测试中)。 2. 为什么在你的测试中使用 Spring Extension 你没有使用任何与 Spring 相关的东西,要么使用它,要么放弃扩展。 3. 从您的测试代码来看,您还混合了 JUnit4 和 JUnit5,这也无济于事。
  • @M.Deinum 它仍然没有解决错误:C
  • 主要原因是混合了 JUnit4 和 JUnit5,这是等待发生的麻烦。如果你不解决它,它根本就行不通。

标签: java spring-boot api rest mocking


【解决方案1】:

您的测试中的主要问题是您在一个测试中混合了 JUnit 4 注释和 JUnit5。 @Before 来自 JUnit4,因此安装程序永远不会运行,因此不会创建依赖项或模拟。因此,NullPointerException 没有注入任何内容。

在您的代码中,您应该使用CompaniaService 接口而不是CompaniaServiceImpl。你已经定义了一个你应该使用的接口。

更换你的控制器。

@RestController
@RequestMapping(path = "/companias")
public class ControllerAPI {


    private final CompaniaService companiaService;

    public ControllerAPI(CompaniaService companiaService) {
        this.companiaService = companiaService;
    }

您不需要字段上的@Autowired,因为注入是通过构造函数完成的!。

现在你可以为你的测试做几件事。

  1. @Before 替换为@BeforeEach,这是用于标记方法的JUnit5 注释。您不需要@ExtendsWith@ContextConfiguration

  2. 使用MockitoExtension,而不是@ExtendsWith(SpringExtension.class),使用@BeforeEach,但不要初始化模拟。

  3. 使用@WebMvcTest@MockBean,让Spring Boot 为您处理注入和模拟创建。

使用正确的注释

class ControllerAPITest {

    String token = "xxx";

    @InjectMocks
    private ControllerAPI controllerAPI;

    @Mock
    private CompaniaService companiaService;

    private MockMvc mockMvc;

    @BeforeEach
    public void setup() throws Exception{

        MockitoAnnotations.initMocks(this);

        this.mockMvc = MockMvcBuilders.standaloneSetup(controllerAPI).build();
    }


   @Test
   void getByField() throws Exception {  ... }

使用MockitoExtension

@ExtendsWith(MockitoExtension.class)
class ControllerAPITest {

    String token = "xxx";

    @InjectMocks
    private ControllerAPI controllerAPI;

    @Mock
    private CompaniaService companiaService;

    private MockMvc mockMvc;

    @BeforeEach
    public void setup() throws Exception{
        this.mockMvc = MockMvcBuilders.standaloneSetup(controllerAPI).build();
    }

   @Test
   void getByField() throws Exception {  ... }

使用@WebMvcTest

@WebMvcTest(ControllerAPI.class)
class ControllerAPITest {

    String token = "xxx";

    @MockBean
    private CompaniaService companiaService;

    @Autowired
    private MockMvc mockMvc;


   @Test
   void getByField() throws Exception {  ... }

【讨论】:

  • 谢谢,但是在您的更改之后会发生两件事,使用 mockitoExtension 它会抛出这个:org.springframework.web.util.NestedServletException:请求处理失败;嵌套异常是 java.lang.NullPointerException... 使用 WebMvcTest 它会抛出:java.lang.IllegalStateException: Failed to load ApplicationContext
  • 如果它抛出了那些你没有告诉所有需要被告知的异常,或者你的第二个模拟不完整(如果有多个调用一个你需要模拟所有的服务他们)。或者您仍在测试中混合 junit 依赖项)。
  • 哦,你的意思是像存储库一样? (我的服务使用 3...)抱歉,这是我第一次测试 MVC 应用程序...
  • 不,至于您的服务,您应该按照我所说的那样使用界面,并且在代码中也这样做!。
  • 好的,抱歉,非常感谢,ExtendWithMockitop 工作,我将Service 更改为控制器中的接口,但我忘记在控制器的构造函数中也进行更改。我的错!!非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
相关资源
最近更新 更多