弹簧测试服务自动装配导致null



我能够运行Rest Controller PUT方法,该方法通过Spring Boot应用程序使用Autowired @Service。在尝试执行Spring JUnit测试时,同样的自动装配失败了。我试着阅读多个类似问题的线程。我确保我没有通过"new"关键字创建@Service,我尝试了上下文配置和其他方法。但一切似乎都是徒劳的。我不确定我哪里出错了。

My Spring Boot Application class -

@SpringBootApplication    
@ComponentScan({    
        "com.initech.myapp.*"    
        })    
public class IngestionServerApplication {    
    private static final Log logger = LogFactory.getLog(IngestionServerApplication.class);    
    public static void main(String[] args) {    
        SpringApplication.run(IngestionServerApplication.class, args);    
        logger.info("Ingestion Server Application started...");    
    }    
}

Rest Controller Class -

package com.initech.myapp.ingestion.controller;  
@RestController  
public class IngestionController extends BaseRestController {  
    private static final Log logger = LogFactory.getLog(IngestionController.class);  
    // This variable is getting "null" autowiring if invoked 
    // via Spring Unit Testing framework while it is injected fine via
    // Spring Boot app invocation.
    @Autowired  
    public IngestionPayloadProcessor payloadProcessor;
    @RequestMapping(path = "/ingest", method = RequestMethod.PUT,  
            consumes = {  
                    MediaType.APPLICATION_JSON_VALUE,  
                    MediaType.APPLICATION_XML_VALUE  
            },  
            produces = {  
                    MediaType.APPLICATION_JSON_VALUE  
            })  
    public IngestionSuccessResponse ingest(@RequestHeader(value = "authToken", required = true) String authToken,                          
                         @RequestBody String jsonBody) throws Exception  {  
        IngestionPayload ingestionPayload = new IngestionPayload();  
        ingestionPayload.setAuthToken(authToken);  
        ingestionPayload.setJsonBody(jsonBody);  
        IngestionSuccessResponse ingestionSuccessResponse = payloadProcessor.process(ingestionPayload);  
        return ingestionSuccessResponse;  
    }  
}
服务类

package com.initech.myapp.ingestion.app.service;
@Service
@ImportResource({"spring.xml"})
public class IngestionPayloadProcessor {
    private static final Log logger = LogFactory.getLog(IngestionPayloadProcessor.class);
    @Resource(name = "kafkaConfig")
        private Properties kafkaConfig;
    @Value("${kakfaTopic}")
        private String kakfaTopic;
    public IngestionSuccessResponse process(IngestionPayload ingestionPayload) throws Exception {
            try {
            IngestionSuccessResponse ingestionSuccessResponse = buildSuccessResponse(ingestionPayload);
            return ingestionSuccessResponse;
                    }
                catch (IllegalStateException e)
                {
                    logger.error("Encountered exception while dropping message in Kafka... " + e.getMessage());
                        throw e;
                    }
            }
            }
private buildSuccessResponse() { ... }

Spring Unit Testing

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath*:/spring.xml")
@WebMvcTest(IngestionServerApplication.class)
public class IngestionServerApplicationTests {
    @Autowired
    private MockMvc mockMvc;
    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.standaloneSetup(
                    new IngestionServiceController())
                            .build();
                }
    @Test
    public void testIngestService() throws Exception {
        HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.add("authToken","safdafio12312asdfs23");
                RequestBuilder requestBuilder = put("/ingest").content("{'testKey' : 'testVal'}").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
        this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
    }
}

错误日志
2016-08-10 19:24:36.500 DEBUG 7505 --- [           main] m.m.a.RequestResponseBodyMethodProcessor : Read [class java.lang.String] as "application/json" with [org.springframework.http.converter.StringHttpMessageConverter@49aa766b]
2016-08-10 19:24:36.510 DEBUG 7505 --- [           main] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public com.initech.myapp.ingestion.model.IngestionSuccessResponse com.initech.myapp.ingestion.app.controller.myappIngestionServiceController.ingest(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String) throws java.lang.Exception]: java.lang.NullPointerException
2016-08-10 19:24:36.512 DEBUG 7505 --- [           main] .m.m.a.ExceptionHandlerExceptionResolver : Invoking @ExceptionHandler method: public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> com.initech.myapp.base.controller.BaseRestController.handleException(java.lang.Exception,javax.servlet.http.HttpServletRequest)
This is the error handler...
2016-08-10 19:24:36.514  INFO 7505 --- [           main] p.d.i.a.c.myappIngestionServiceController : > handleNoResultException
2016-08-10 19:24:36.574 DEBUG 7505 --- [           main] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{status=500, authToken=6acb1a5c-2ced-4690-95b3-eb7957c7c28a, error=null}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@50d3bf39]
java.lang.AssertionError: Status 
Expected :200
Actual   :500

请注意,我已经通过测试进行了调试,并且我可以看到,由于payloadProcessor对象为空,在Rest控制器类的下一行抛出了NullPointer异常。

IngestionSuccessResponse ingestionSuccessResponse = payloadProcessor.process(ingestionPayload);

= = = = =

使用Mock对象进行单元测试:(这按预期工作)

@RunWith(SpringRunner.class)
@ActiveProfiles("dev")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class IngestionServerUnitTests {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private IngestionPayloadProcessor processor;
    // I was able to get this to work by removing the setUp() method 
    // that was originally in my code. It was trying to build a new instance
    // of the REST controller and then run the "perform" on top of it
    // which was causing the test to fail I assume!
    /*@Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.standaloneSetup(
                new IngestionServiceController())
                .build();
    }*/
    @Test
    public void testIngestService() throws Exception {
        IngestionSuccessResponse ingestionSuccessResponse = new IngestionSuccessResponse();
        ingestionSuccessResponse.setStatusCode("200");
        ingestionSuccessResponse.setRequestId("6acb1a5c-2ced-4690-95b3-eb7957c7c28a");
        ingestionSuccessResponse.setReceivedTimestamp("2016-08-09T19:43:30.02234312");
        given(this.processor.process(anyObject())).willReturn(ingestionSuccessResponse);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Authorization","5e18685c95b34690");
        RequestBuilder requestBuilder = put("/ingest").content("<test>test data</test>").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
        this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
    }
}

当您指定@WebMvcTest时,只有应用程序的某些组件被添加到ApplicationContext中。该注释实际上是文档中描述的一堆其他注释的组合:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html

基于此,您的IngestionPayloadProcessor不会被实例化为bean,并且不应该,因为您告诉测试只运行web层的测试。您需要做的是在测试中为IngestionPayloadProcessor指定一个@MockBean,然后为控制器正在调用的方法定义一个mock。

@RunWith(SpringRunner.class)
@WebMvcTest(IngestionServerApplication.class)
public class IngestionServerApplicationTests {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private IngestionPayloadProcessor processor;
    @Test
    public void testIngestService() throws Exception {
        given(this.processor.process(anyObject())).willReturn(new InjestionSuccessResponse());
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("authToken","safdafio12312asdfs23");
        RequestBuilder requestBuilder = put("/ingest").content("{'testKey' : 'testVal'}").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
        this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
    }
}

关于Spring Boot 1.4测试新特性的详细信息在这里:https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

*基于注释更新*

意识到您可以自动配置MockMvc,而不需要使用TestRestTemplate。我还没有测试过,但应该可以。

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class IngestionServerApplicationTests {
    @Autowired
    private MockMvc mockMvc;
    @Test
    public void testIngestService() throws Exception {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("authToken","safdafio12312asdfs23");
        RequestBuilder requestBuilder = put("/ingest").content("{'testKey' : 'testVal'}").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
        this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
    }
}

最新更新