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

时间:2014-05-22 16:58:53

标签: java spring unit-testing junit mocking

所以我有这个测试用例:

@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"));
}

}

加载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>

希望能够将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;
}
}

但我的单元测试正在抛出以下痕迹:

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

当Controller尝试访问customerService时抛出此空指针:

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

为什么我的MockCustomerServiceImpl没有被自动连接到控制器?

注意:包名已被更改以保护无辜的

3 个答案:

答案 0 :(得分:4)

来自Spring documentation

  

“webAppContextSetup”加载实际的Spring MVC配置,从而产生更完整的集成测试。由于TestContext框架缓存了加载的Spring配置,因此即使添加了更多测试,它也有助于保持测试快速运行。此外,您可以通过Spring配置将模拟服务注入控制器,以便继续专注于测试Web层。

     

另一方面,“standaloneSetup”更接近单元测试。它一次测试一个控制器,控制器可以手动注入模拟依赖项,并且不涉及加载Spring配置。这些测试更侧重于样式,并且更容易查看正在测试哪个控制器,是否需要任何特定的Spring MVC配置,等等。 “standaloneSetup”也是编写临时测试以验证某些行为或调试问题的一种非常方便的方法。

据此我明白,如果我需要一个.xml作为要加载的Spring配置文件,那么我需要使用“webAppContextSetup”。如果我只需要使用我的Controller进行非常简单的测试,而不需要Spring配置,那么我就采用“standaloneSetup”方法。在您的情况下,我认为您需要“webAppContextSetup”。

答案 1 :(得分:3)

您忘记创建模拟对象,然后将其注入控制器,如此

@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"));
    }
}

答案 2 :(得分:0)

它通过添加注释 @RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)@AutoConfigureMockMvc

帮助我修复了 NullPointerException

这是对我有用的代码-

@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());
              
    }