해당 포스팅은 테스트 시 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
'Server > JUnit, Spock' 카테고리의 다른 글
JUnit - TestContainers 사용하는 방법! (+ 장단점 비교) (0) | 2022.06.06 |
---|---|
Mockito로 BDD 테스트 코드 작성하기 (BDDMockito) (0) | 2022.06.05 |
Mockito를 제대로 사용하는 방법들! (단위테스트) (2) | 2022.06.04 |
JUnit5 - 단위 테스트에서 프로퍼티 불러오기 (0) | 2022.06.02 |
JUnit5 - Parameterized Tests (0) | 2022.05.29 |