Spring Bootアプリケーションを介して、期待どおりにAutowired @ Serviceを使用するRestControllerPUTメソッドを実行できます。 Spring JUnitテストを実行しようとしているときに、同じ自動配線が失敗します。同様の問題で複数のスレッドを読んでみました。 「new」キーワードを使用して@Serviceを作成していないことを確認し、コンテキスト構成やその他の方法を試しましたが、すべてが無駄になっているようです。どこが間違っているのかわかりません。
私のSpringBootアプリケーションクラス-
@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コントローラークラス-
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() { ... }
スプリングユニットテスト
@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オブジェクトがnullであるため、RestControllerクラスの次の行にNullPointer例外がスローされていることがわかります。
IngestionSuccessResponse ingestionSuccessResponse = payloadProcessor.process(ingestionPayload);
=====
@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
を指定してから、コントローラーが呼び出しているメソッドのモックを定義する必要があります。
@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());
}
}