简体   繁体   English

junit:使用 mockmvc 测试控制器时注入的 bean 上的 NullPointer

[英]junit: NullPointer on injected bean when testing controller with mockmvc

So I have this test case:所以我有这个测试用例:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:rest-context-test.xml")
public class CustomerControllerTest {

private MockMvc mockMvc;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders
            .standaloneSetup(new CustomerController()).build();
}


@Test
public void testGetCustomerById() throws Exception {
    mockMvc.perform(get("/customers/{ccid}", 1)).andExpect(status().isOk())
            .andExpect(content().contentType("application/json"))
            .andExpect(jsonPath("$.ccid").value("1"));
}

}

That loads rest-context-test.xml:加载 rest-context-test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">

<bean id="customerServiceImpl"
    class="myorg.service.impl.MockCustomerServiceImpl" />

</beans>

With the hope of being able to AutoWire customerServiceImpl into my controller:希望能够将 AutoWire customerServiceImpl 添加到我的控制器中:

@Controller
@RequestMapping("customers")
public class CustomerController {

private static Logger Logger = LoggerFactory
        .getLogger(CustomerController.class);

@Autowired
@Qualifier("customerServiceImpl")
private CustomerService customerService;


@RequestMapping(value = "/{ccid}", method = RequestMethod.GET, produces = "application/json")
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public Customer getCustomerById(@PathVariable Integer ccid)
        throws BadRequestException, ServerErrorException,
        ResourceNotFoundException {
    Customer c = null;

    try {
        c = customerService.getCustomer(ccid);
    } catch (IllegalArgumentException iae) {
        // HTTP 400
        Logger.error("Invalid arguments submitted", iae);
        throw new BadRequestException();
    } catch (CsspException ce) {
        // HTTP 500
        Logger.error("Server error", ce);
        throw new ServerErrorException();
    }

    if (c == null) {
        // HTTP 404
        throw new ResourceNotFoundException();
    }

    return c;
}
}

But my unit test is throwing the following trace:但我的单元测试抛出以下跟踪:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:965)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:66)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:168)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:136)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:141)
at myorg.rest.controller.CustomerControllerTest.testGetCustomerById(CustomerControllerTest.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.NullPointerException
at myorg.rest.controller.CustomerController.getCustomerById(CustomerController.java:134)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle    (ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod    (RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal    (RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
... 38 more

This null pointer is thrown when the Controller tries to access customerService:当 Controller 尝试访问 customerService 时会抛出此空指针:

@Autowired
@Qualifier("customerServiceImpl")
private CustomerService customerService;

Why isn't my MockCustomerServiceImpl being AutoWired into the Controller?为什么我的 MockCustomerServiceImpl 没有自动连接到控制器中?

Note: package names have been changed to protect the innocent注意:包名已更改以保护无辜者

From Spring documentation :Spring 文档

The "webAppContextSetup" loads the actual Spring MVC configuration resulting in a more complete integration test. “webAppContextSetup”加载实际的 Spring MVC 配置,从而产生更完整的集成测试。 Since the TestContext framework caches the loaded Spring configuration, it helps to keep tests running fast even as more tests get added.由于 TestContext 框架缓存加载的 Spring 配置,因此即使添加了更多测试,它也有助于保持测试快速运行。 Furthermore, you can inject mock services into controllers through Spring configuration, in order to remain focused on testing the web layer.此外,您可以通过 Spring 配置将模拟服务注入控制器,以便专注于测试 web 层。

The "standaloneSetup" on the other hand is a little closer to a unit test.另一方面,“standaloneSetup”更接近于单元测试。 It tests one controller at a time, the controller can be injected with mock dependencies manually, and it doesn't involve loading Spring configuration.它一次测试一个控制器,控制器可以手动注入模拟依赖项,并且不涉及加载 Spring 配置。 Such tests are more focused in style and make it easier to see which controller is being tested, whether any specific Spring MVC configuration is required to work, and so on.这样的测试在风格上更集中,并且更容易查看正在测试的控制器,是否需要任何特定的 Spring MVC 配置,等等。 The "standaloneSetup" is also a very convenient way to write ad-hoc tests to verify some behavior or to debug an issue. “standaloneSetup”也是编写临时测试以验证某些行为或调试问题的一种非常方便的方法。

From this I understand that, if I need an .xml as a Spring configuration file to be loaded, then I need to go with the "webAppContextSetup".由此我明白,如果我需要一个 .xml 作为要加载的 Spring 配置文件,那么我需要使用“webAppContextSetup”。 If I just need very simple tests with my Controller, without the need of a Spring configuration, then I go with the "standaloneSetup" approach.如果我只需要对我的 Controller 进行非常简单的测试,而不需要 Spring 配置,那么我会使用“standaloneSetup”方法。 In your case, I think you need the "webAppContextSetup".就您而言,我认为您需要“webAppContextSetup”。

You forgot to create your mock objects and then inject them into your controller like so您忘记创建模拟对象,然后像这样将它们注入控制器

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:rest-context-test.xml")
public class CustomerControllerTest 
{
    @Mock
    private CustomerService customerService;
    @InjectMocks
    private CustomerController customerController;

    private MockMvc mockMvc;

    @Before
    public void setup() 
    {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders
                .standaloneSetup(customerController).build();
    }


    @Test
    public void testGetCustomerById() throws Exception 
    {
        mockMvc.perform(get("/customers/{ccid}", 1)).andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.ccid").value("1"));
    }
}

It helped me to fix the NullPointerException by adding annotations @RunWith(SpringRunner.class) , @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) and @AutoConfigureMockMvc它通过添加注释@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)@AutoConfigureMockMvc帮助我修复了 NullPointerException

Here is code that worked for me-这是对我有用的代码-

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MyTestClass {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void myClassController() throws Exception {

        Object obj = new Object(); 

        obj.setA(1);
        obj.setB(2);
        obj.setC(3);

        ObjectMapper mapper = new ObjectMapper();

        String jsonString = mapper.writeValueAsString(obj); // this is the json body
 
        mockMvc.perform(post("/ENDPOINT")
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonString))
                .andDo(print())
                .andExpect(status().isOk());
              
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM