Posted at

TestRestTemplate の Autowired にて NoSuchBeanDefinitionException が発生する

More than 1 year has passed since last update.


現象

下記のようなクラス構成のテストを書いたところ、TestRestTemplate が Autowired できない現象が発生しました。


application.properties

# AP は 9000 番ポートで動作させる

server.port=9000


AbstractBaseTest.java

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = HogeApplication.class)
public abstract class AbstractBaseTest {

protected MockMvc mvc;

@Autowired
protected WebApplicationContext context;

@Autowired
protected TestRestTemplate restTemplate;



SubTestClass.java

@RunWith(SpringRunner.class)

@SpringBootTest
@ContextConfiguration(classes = HogeApplication.class)
public class SubTestClass extends AbstractBaseTest {
something tests...
public void URLをテストする() throws Exception {
HashMap param = new HashMap<String, String>();
param.put("userAccount", ユーザーアカウント);
param.put("password", パスワード);
ResponseEntity<String> resultResponse = restTemplate.postForEntity("http://localhost:9000/authentication", param, String.class);

}
}


ログの内容


Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.test.web.client.TestRestTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 28 more


回避策

サブクラスにて webEnvironment に RANDOMPORT、親クラスなどで@LocalServerPortを指定するです。

Spring Boot のページ のサンプルでは、LocalServerPort の指定があり、テストをするにあたり事実上不可欠のアノテーションですが、TestRestTemplate の生成については不要のようです。ドキュメントにも必須かどうかについての言及がありません。


Note the use of webEnvironment=RANDOM_PORT to start the server with a random port (useful to avoid conflicts in test environments), and the injection of the port with @LocalServerPort. Also note that Spring Boot has provided a TestRestTemplate for you automatically, and all you have to do is @Autowired it.


2017/08 現在の動作としては、


  • 使用するクラスにて TestRestTemplate を Autowired しないと Bean を生成できない。

  • LocalServerPort のポートへ TestRestTemplate から要求しないと、Connection Refused になる。

のようです。

よって正しい構成は下記のようになるようです。


AbstractTest.java

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) //あってもなくても良い
@ContextConfiguration(classes = HogeApplication.class)
public abstract class AbstractBaseTest {

protected MockMvc mvc;

@Autowired
protected WebApplicationContext context;

@Autowired
protected TestRestTemplate restTemplate;

@LocalServerPort
protected int serverPort;
}



SubTestClass.java

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = HogeApplication.class)
public class SubTestClass extends AbstractBaseTest {
something tests...
public void URLをテストする() throws Exception {
HashMap param = new HashMap<String, String>();
param.put("userAccount", ユーザーアカウント);
param.put("password", パスワード);
ResponseEntity<String> resultResponse = restTemplate.postForEntity("http://localhost:" + serverPort + "/authentication", param, String.class);

}
}


@LocalServerPort が存在しない場合、該当のアプリケーションにモックの HTTP 経由でアクセスできないようです。

ConnectionRefused のエラーが発生します。


org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:9000/hoge": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)

at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:407)
at org.springframework.boot.test.web.client.TestRestTemplate.postForEntity(TestRestTemplate.java:471)
at com.asaas.billing.controller.SigninControllerTest.URLをテストする(SigninControllerTest.java:72)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)

Caused by: java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)