Closed ghkdqhrbals closed 7 months ago
테스트만을 위한 설정이 필요한걸까요?!
넵. 외부 API 와 연동하는 테스트를 진행할려면 이 부분 해결이 필요할 것 같습니다.
해결완료했습니다.
알고보니 TemplateUtil 의 createRequest 메소드에서 testTemplate.getUrl() 에 webClient 요청을 보내고 있었더라구요ㅜㅜ. 저는 왜 자꾸 생성자를 통한 외부 의존성 주입을 진행했는데도 localhost:8080 으로 요청을 전달하지?
라는 의문을 가지고 있었는데, 기존 설정된 url 로 전송하도록 되어있는 createRequest 메소드 내부 로직으로 인해 그런거였습니다...
이제 테스트 코드 내부에서 template 을 생성할 때, url 을 mockServer 의 url 로 설정한 뒤 테스트하시면 될 것 같습니다 :) 아래는 예시입니다.
...
@BeforeEach
public void setUpEach() {
// webClient 를 따로 mock 서버 url 로 설정합니다.
WebClient webClient = WebClient.builder().baseUrl(mockBackEnd.url("/").toString()).build();
// 생성자를 통해 따로 webClient 를 주입합니다.
testResultService = new TestResultService(testTemplateRepository, testResultRepository,
tpsRepository, mttfbRepository, testErrorLogRepository, userGroupRepository,
webClient, new RequestCounter());
}
...
@Test
@DisplayName("성능 측적 method 호출 시 결과 저장 후 반환 확인하는 테스트")
public void getMethodTemplateResultTest() throws InterruptedException {
//given
Optional<TestTemplateResponseDto> template = saveGetTempData(mockBackEnd.url("/").toString());
// 반환하고자 하는 stub 응답들을 응답큐에 삽입합니다.
mockBackEnd.enqueue(new MockResponse().setBody("Hello, World0"));
mockBackEnd.enqueue(new MockResponse().setBody("Hello, World1"));
mockBackEnd.enqueue(new MockResponse().setBody("Hello, World2"));
mockBackEnd.enqueue(new MockResponse().setBody("Hello, World3"));
mockBackEnd.enqueue(new MockResponse().setBody("Hello, World4"));
//when
TestResultResponseDto testResultResponseDto = testResultService.measurePerformance(
"userGroup", template.get().getId(), "start");
//then
assertThat(testResultResponseDto.getMethod()).isEqualTo(template.get().getMethod());
assertThat(testResultResponseDto.getUrl()).isEqualTo(template.get().getUrl());
assertThat(testResultResponseDto.getTotalUsers()).isEqualTo(template.get().getVuser());
}
좀 더 쓰기 편하도록 클래스를 만들고 이를 테스트 클래스가 extends 하도록 설정하였습니다.
/**
* webClient 요청을 받을 mock 서버를 생성합니다
*/
public class MockServer {
/**
* Mock server instance
*/
public static MockWebServer mockBackEnd;
/**
* Opened mock server url
*/
public static String backendUrl;
private ObjectMapper objectMapper = new ObjectMapper();
@BeforeAll
static void setUp() throws IOException {
// webClient 요청을 받을 mock 서버를 생성합니다
mockBackEnd = new MockWebServer();
mockBackEnd.start();
backendUrl = String.format(mockBackEnd.url("/").toString());
}
@AfterAll
static void tearDown() throws IOException {
mockBackEnd.shutdown();
}
/**
* Mock server에 응답을 추가합니다
*
* <p> 만약 Object 를 Json String 으로 변환할 수 없다면 "" 값이 응답으로 반환됩니다</p>
*
* @param object
*/
public void addMockResponse(Object object) {
String json = "";
try {
json = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
MockResponse response = new MockResponse()
.setHeader("Content-Type", "application/json")
.setBody(json);
mockBackEnd.enqueue(response);
}
/**
* Mock server에 응답을 추가합니다. 반복 횟수를 지정할 수 있습니다.
*
* <p> 만약 Object 를 Json String 으로 변환할 수 없다면 "" 값이 응답으로 반환됩니다</p>
*
* @param object
* @param repeatCount
*/
public void addMockResponse(Object object, int repeatCount) {
String json = "";
try {
json = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
MockResponse response = new MockResponse()
.setHeader("Content-Type", "application/json")
.setBody(json);
for (int i = 0; i < repeatCount; i++) {
mockBackEnd.enqueue(response);
}
}
}
테스트 클래스
class TestResultServiceTest extends MockServer {
...
@Test
@DisplayName("성능 측적 method 호출 시 결과 저장 후 반환 확인하는 테스트")
public void getMethodTemplateResultTest() throws InterruptedException {
//given
Optional<TestTemplateResponseDto> template = saveGetTempData(mockBackEnd.url("/").toString());
// mockBackend 에 아래의 응답을 10번 반복하여 추가합니다
addMockResponse("test-response-1",10);
//when
TestResultResponseDto testResultResponseDto = testResultService.measurePerformance(
"userGroup", template.get().getId(), "start");
//then
assertThat(testResultResponseDto.getMethod()).isEqualTo(template.get().getMethod());
assertThat(testResultResponseDto.getUrl()).isEqualTo(template.get().getUrl());
assertThat(testResultResponseDto.getTotalUsers()).isEqualTo(template.get().getVuser());
}
}
집가서 더 보긴할텐데,, 제가 오늘 아침에 확인했을 때 bm-common 폴더가 있는데 bm-agent에서 자꾸 빌드를 실패하고 있던데 이건 뭔가 설정이 따로 필요한걸까요?
제가 develop 그대로 pull 로 가져와서 확인해봐도 마찬가지더라구요!
추가로 외부 api테스트를 진행 할 때 어떤걸 호출해야 적절한지 잘 모르겠어서 우선 규민님이 작성해주신 유저 api랑 제가 만든 템플릿 api랑 호출하는 식으로 테스트를 진행했었습니다!!
mockserver를 만들어서 테스트 해야 하는 부분까지는 제가 잘 몰랐던 부분이였어요. 좋은 정보 감사합니다!!
집가서 더 보긴할텐데,, 제가 오늘 아침에 확인했을 때 bm-common 폴더가 있는데 bm-agent에서 자꾸 빌드를 실패하고 있던데 이건 뭔가 설정이 따로 필요한걸까요?
제가 develop 그대로 pull 로 가져와서 확인해봐도 마찬가지더라구요!
아마 gradle 싱크 맞추실 때 깨져있을 수도 있습니다.
루트 프로젝트가 bm-agent, bm-common 을 포함하고 있어야합니다. 이를 위해서는 루트의 settings.gradle 에 include ':bm-agent', include ':bm-common' 를 적어주어야합니다.
그리고 루트 프로젝트 밖에 따로 선언되어있다면 intellij 가 따로 잡아놓은 것이니 이를 -
버튼으로 제거해주면 됩니다.
추가로 외부 api테스트를 진행 할 때 어떤걸 호출해야 적절한지 잘 모르겠어서 우선 규민님이 작성해주신 유저 api랑 제가 만든 템플릿 api랑 호출하는 식으로 테스트를 진행했었습니다!!
mockserver를 만들어서 테스트 해야 하는 부분까지는 제가 잘 몰랐던 부분이였어요. 좋은 정보 감사합니다!!
넵! 쓰기 편하도록 MockServer 를 따로 설정해서 편하게 쓰실 수 있또록 만들었어요ㅎㅎ
현재 저희는 WebClientConfig 에서 WebClient 를 빈으로 등록하고 TestResultService 에서 이를 주입받아서 사용하고 있습니다. 그리고 이 WebClient 의 baseUrl 은 localhost:8080 으로 설정되어있죠. 그래서 TestResultService 를 테스트 하기 위해서는 localhost:8080 에 대한 요청과 응답을 모킹하여 진행해야합니다.
그래서 한가지 방법을 가져와보았는데요. 바로
MockWebServer
를 통해서 (1) 모킹 서버를 열고(랜덤포트), 해당 서버로부터 들어오는 (2) 요청과 응답을 사전에 설정할 수 있는okhttp3.mockwebserver
라이브러리를 사용해보았습니다.아래와 같이 코드를 작성하게 된다면 문제없이 진행되었습니다. 응답으로
Hello, World!
가 도착합니다.하지만 !
저희는 TestResultService 가 주입받는 WebClient 를 모킹서버의 주소로 바꿔주어야합니다. 그래서 생성자를 통해 주입시켰는데도 여전히 모킹서버 주소(localhost:랜덤포트)가 아닌, 기존 WebClient 빈의 baseUrl 인 localhost:8080 로 요청을 전송합니다ㅜㅜ.
@LeeJeongGi Help!!!!!