본문 바로가기
spring boot/api

Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has been bound to more than one RestTemplate | RestTemplate 테스트 에러

by junjunjun 2024. 3. 26.
반응형

MockRestServiceServer를 이용하여 RestTemplate 테스트를 진행하던 도중 만나게 된 에러입니다.

MockRestServiceServer는 우리에게 테스트를 진행할 수 있도록 가상의 서버를 제공해 줍니다. 하지만 해당 서버는 하나의 RestTemplate만을 바인딩될 수 있으며 하나 이상일 경우 위와 같은 에러가 발생하게 됩니다.

 

아래는 에러 발생지입니다.

@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.test.webclient.mockrestserviceserver", name = "enabled")
public class MockRestServiceServerAutoConfiguration { 

	//... 생략 ...
        
        // 에러 발생지
        private RequestExpectationManager getDelegate() {
            Map<RestTemplate, RequestExpectationManager> expectationManagers = this.customizer.getExpectationManagers();
            Assert.state(!expectationManagers.isEmpty(), "Unable to use auto-configured MockRestServiceServer since "
                    + "MockServerRestTemplateCustomizer has not been bound to a RestTemplate");
            Assert.state(expectationManagers.size() == 1, "Unable to use auto-configured MockRestServiceServer since "
                    + "MockServerRestTemplateCustomizer has been bound to more than one RestTemplate"); // 여기
            return expectationManagers.values().iterator().next();
        }
}

 

에러 발생 상황

하나의 테스트 케이스에서 두 개의 restTempate api요청을 설정해 줍니다.

 

@RestClientTest(value = {OauthProvider.class, OtherProvider.class})
class OauthProviderTest {


    @Autowired
    private OauthProvider oauthProvider;

    @Autowired
    private MockRestServiceServer mockServer;

    @Test
    @DisplayName("oauth2 로 멤버 조회에 성공한다.")
    void getMemberInfo_success() throws JsonProcessingException {

        ... 생략
        mockServer.expect(MockRestRequestMatchers.requestTo("테스트 url-1"))
                .andRespond(MockRestResponseCreators.withSuccess(expectResult, MediaType.APPLICATION_JSON));

        ... 생략
        mockServer.expect(MockRestRequestMatchers.requestTo("테스트 url-2"))
                .andRespond(MockRestResponseCreators.withSuccess(expectResult2, MediaType.APPLICATION_JSON));


        // when
        MemberInfo memberInfo = oauthProvider.getMemberInfo("code");

		... 생략
    }

}
  • oauthProvider.getMemberInfo() 메서드 내부에는 "테스트 url-1"와 "테스트 url-2"으로 총 2개의 api 요청이 들어있습니다.
  • "테스트 url-1"의 요청 로직은 oauthProvider 클래스에 있습니다.
  • "테스트 url-2"의 요청 로직은 otherProvider 클래스에 있으며 oauthProvider에서 주입받아서 사용하고 있습니다.

 

아래는 oauthProvider와 otherProvider 코드입니다.

@Component
class oauthProvider {

    public OauthProvider(OauthCacheProvider oauthCacheProvider, RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }
    // 테스트 중인 메서드
    public MemberInfo getMemberInfo(String code) {
    	...
        
        restTemplate.exchange("테스트 url-1")
        
        otherProvider.requestApiTestTwo()
        
        ...
    }
}

@Component
class OtherProvider {

    public OtherProvider(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }
    
    public void requestApiTestTwo() {
    	restTemplate.exchange("테스트 url-2")
    }
}

 

각 클래스에 별도의 RestTemplate 객체를 RestTemplateBuilder를 사용하여 생성해 주었습니다.

각각 따로 생성해 준 것이 에러의 원인이었습니다.

 

위에서 말씀드렸다시피 MockRestServiceServer는 하나의 RestTemplate와 바인딩이 되어야 하는데 위의 테스트 코드를 진행시키면 OauthProvider 클래스의 RestTemplate와 OtherProvider 클래스의 RetsTemplate 2개가 바인딩이 되어 에러가 발생하게 됩니다.

 

해결방법

테스트 코드를 분리해 줘도 되지만 그냥 RestTemplate을 빈으로 등록시킨 뒤 주입받아서 사용해 주면 됩니다. (싱글톤)

@Configuration
public class RestTemplateConfig{

    @Bean
    RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        return restTemplateBuilder.build();
    }
}

@Component
class oauthProvider {

    public OauthProvider(OauthCacheProvider oauthCacheProvider, RestTemplate restTemplate) { // 수정
        this.restTemplate = restTemplate; // 수정
    }
}

@Component
class OtherProvider {

    public OtherProvider(RestTemplate restTemplate) { // 수정
        this.restTemplate = restTemplate; // 수정
    }
}

 

다만 이렇게 사용할 경우 @RestClientTest 안에 RestTemplate 관련 빈을 넣어줘야 됩니다.

@RestClientTest(value = {OauthProvider.class, OtherProvider.class, RestTemplateBuilder.class, RestTemplateConfig.class})
class OauthProviderTest {
	...
}

 

반응형

댓글