Help us understand the problem. What is going on with this article?

TestRestTemplate の Autowired にて NoSuchBeanDefinitionException が発生する

More than 3 years have 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)
kakasak
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away