Server/JUnit, Spock

Spring - OpenFeign을 Mocking하는 방법!

JaeHoney 2022. 6. 2. 12:40

해당 포스팅은 테스트 시 OpenFeign의 FeignClient를 Mocking 하는 방법에 대해 다룬다.

Product 코드

실제 코드단의 OpenFeign 코드이다. 매우 심플하다. 먼저 인터페이스를 보자.

public interface CacheApiClient {
    Office getOfficeSetting(long officeNo);
}

인터페이스를 사용하는 이유는 DIP 원칙을 지키기 위해서이다. 클라이언트를 필요에 따라 WebClient나 새로운 클라이언트로 변경이 가능하게 작성하기 위해서 사용처에서 인터페이스에 의존한다.

 

구현체는 아래와 같다.

@FeignClient(name = "cache-api", url = "${domain.apis.cache-api}")
public interface FeignCacheApiClient extends CacheApiClient {

    @GetMapping(path = "/offices/{no}")
    Office getOfficeSetting(@PathVariable("no") long no);

}

이제 Controller 또는 사용처에서 해당 인터페이스의 Bean을 주입받아서 사용하면 된다.

@RestController
@RequiredArgsConstructor
public class MailboxController {

    private final CacheApiClient cacheApiClient;
    
    // .. 생략

    @PostMapping
    public ApiResponse createMailbox(@AuthenticationPrincipal User user) {
        Office office = cacheApiClient.getOfficeSetting(user.getOfficeNo());
        // .. 생략
    }
}

Test  코드

이제 테스트를 작성해보자.

 

먼저 해당 라이브러리의 의존성을 추가한다.

testImplementation 'com.github.tomakehurst:wiremock:2.27.2'

 

그리고 TestConfiguration에서 WireMockServer를 Bean으로 등록한다.

@TestConfiguration
public class MockCacheApiConfig {

    @Bean(initMethod = "start", destroyMethod = "stop")
    public WireMockServer mockCacheApi() {
        return new WireMockServer(9561);
    }

}

그리고 테스트 환경에서 FeignClient의 url로 주입받을 property를 설정한다. (application-test.yml)

domain:
  apis:
    cache-api: http://localhost:9561

이후 Mocking 기능을 구현할 Static class를 생성한다.

public class MockCacheApi {

    public static void setupGetOfficeResponse(WireMockServer mockCacheApi) {
        mockCacheApi.stubFor(WireMock.get(WireMock.urlMatching("/offices/[1-9]"))
                .willReturn(WireMock.aResponse()
                        .withStatus(HttpStatus.OK.value())
                        .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
                        .withBodyFile("payload/get-office-response.json")
                )
        );
    }

}

이때 withBody를 사용해서 String(json)을 명시해도 된다. 위에서는 withBodyFile을 사용했는데, 해당 파일은  상대경로를 사용하면 기준이 src/test/resources/__files가 기본 설정이다.

 

반환할 결과값을 json 형태로 저장하면된다. 본인의 경우 Postman으로 dev서버에 요청을 보낸 후 값만 수정해서 그대로 파일로 저장했다.

# src/test/resources/__files/payload/get-office-response.json
{
    "id": 1,
    "db_ip": "127.0.0.1",
    "db_partitioning": "01",
    "mail_partitioning": "01",
}

이제 테스트를 수행할 클래스에서 WireMockServer를 빈으로 등록하는 Config 클래스를 로드한 후에 WireMockServer의 의존성을 주입받고, Mocking을 수행하는 static 메서드를 실행해주기만 하면 된다.

@ContextConfiguration(classes = { MockCacheApiConfig.class })
public class MailboxControllerTests extends BaseControllerTest {

    @Autowired
    WireMockServer mockCacheApi;

    @Test
    @DisplayName("개인 편지함을 생성할 수 있다.")
    void createMailbox_shouldSuccess() throws Exception {
        MockCacheApi.setupGetOfficeResponse(mockCacheApi);
        mockMvc.perform(post("/mailboxes")
            // ..생략
    );

}

해당 메서드는 JUnit5에서 제공하는 @BeforeAll, @BeforeEach 메서드에 포함시켜도 된다. 본인의 경우 FeignClient를 사용하는 메서드가 한정되어 있어서 메서드 테스트 내부에서 호출하였다.

 

물론, 클라이언트의 메서드의 결과값만 모킹해도 상관없다. 다만, 해당 결과가 잘 파싱되어서 값이 정상적으로 원하는 형태로 들어오는 지 여부를 보장할 수 없다. 그래서 외부 Api 자체를 Mocking하는 방법에 대해서 알아봤다.

 


Reference