일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 백준 알고리즘
- BFS
- 코딩봉사
- programmers
- 문제풀이
- 1과목
- SQL
- C++
- 회고
- 코딩교육봉사
- 시나공
- 코틀린
- 자바
- 백준알고리즘
- python
- 데이터베이스
- 스프링
- softeer
- 알고리즘
- 소프티어
- 백준
- SW봉사
- kotlin
- 정보처리산업기사
- 파이썬
- 공부일지
- CJ UNIT
- 프로그래머스
- java
- MYSQL
- Today
- Total
JIE0025
webClient, MockWebServer를 이용한 단위테스트 (kotlin) 본문
✅ 개요
일반적으로 우리는 서비스단의 단위 테스트를 작성할때,
repository를 mock(가짜객체)으로 만들고,
DB로의 흐름을 끊어버리는 단위테스트를 작성했었다.
최근에 외부 API를 사용하기위해 webClient를 사용했는데
이때 서비스 단에서 WebClient를 주입 받아, mock객체를 repository가 아닌 다른 대상에 처음 적용해 보게 되어 이 글을 적게 되었다.
데이터베이스에서 어떤 값을 가져와 일반적인 <데이터> 자체만을 검증했었다면
이번 기회로 더 다양한것을 검증해보게 된 계기가 될 것 같다.
✅ WebClient 자체를 mock객체로 만들어서, 모킹해도 된다.
이 방식에는 문제점이 있다.
아래 예제 코드를 보면
webClientMock.get()에 대한 처리부터,
requestHeadersUriMock.... 등등 다양한 처리가 필요하고 이것은 초보자의 시점에서 굉장히 복잡한 과정이다.
이 방식은 또한 WebClient의 동작 과정을 알아야하는데,
서비스 단의 테스트를 작성하기 위해 WebClient 내부 동작을 알고, stubbing을 해줘야한다는 점에서부터
테스트 메서드의 역할/목적이 명확하지 못하게 된다.
코드 출처 : https://www.baeldung.com/spring-mocking-webclient
@ExtendWith(MockitoExtension.class)
public class EmployeeServiceTest {
@Test
void givenEmployeeId_whenGetEmployeeById_thenReturnEmployee() {
Integer employeeId = 100;
Employee mockEmployee = new Employee(100, "Adam", "Sandler",
32, Role.LEAD_ENGINEER);
when(webClientMock.get())
.thenReturn(requestHeadersUriSpecMock);
when(requestHeadersUriMock.uri("/employee/{id}", employeeId))
.thenReturn(requestHeadersSpecMock);
when(requestHeadersMock.retrieve())
.thenReturn(responseSpecMock);
when(responseMock.bodyToMono(Employee.class))
.thenReturn(Mono.just(mockEmployee));
Mono<Employee> employeeMono = employeeService.getEmployeeById(employeeId);
StepVerifier.create(employeeMono)
.expectNextMatches(employee -> employee.getRole()
.equals(Role.LEAD_ENGINEER))
.verifyComplete();
}
}
위에서 이야기한 단점 때문에
저런 방식은 잘 사용되지 않으며, MockWebServer가 추천되고 있다.
✅ MockWebServer
HTTP Request를 받아, Response로 반환하는 간단한 웹서버이다.
Http를 호출하는 메서드의 테스트코드를 작성할 때
MockWebServer를 이용하면 쉽게 테스트를 작성할 수 있다.
(* 스프링 팀 도 이 친구를 사용하도록 권장하고 있다)
✍️ application.yml
⏺ mockwebserver에 대한 의존성 추가
testImplementation("com.squareup.okhttp3:mockwebserver:4.9.2")
⏺ 검증(StepVerifier)을 위한 의존성 추가
testImplementation("io.projectreactor:reactor-test")
✍️ MyService
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
@Service
class MyService(
private val webClient: WebClient
) {
fun someMethod(): Mono<String> {
return webClient.get()
.uri("/default-url")
.retrieve()
.bodyToMono(String::class.java)
}
}
✍️ MyServiceTest
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.client.WebClient
class MyServiceTest {
private lateinit var mockWebServer: MockWebServer
private lateinit var webClient: WebClient
private lateinit var myService: MyService
@BeforeEach
fun setup() {
//1. MockWebServer의 초기화
mockWebServer = MockWebServer()
mockWebServer.start()
//2. WebClient 생성과 MockWebServer URL 설정을 해준다.
webClient = WebClient.builder().baseUrl(mockWebServer.url("").toString())
//옵션들 추가시 추가해주면 됨
.defaultHeaders { headers ->
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
}
.codecs { configurer ->
configurer.defaultCodecs().maxInMemorySize(5 * 1024 * 1024)
}
.build()
//3. 서비스에 클라이언트 주입
myService = MyService(webClient)
}
@AfterEach
fun teardown() {
//4. 테스트가 종료되면 MockWebServer도 종료시킨다.
mockWebServer.shutdown()
}
@Test
fun testSomeMethod() {
//✅ given
//요청 url
val url = "/default-url"
// 응답 데이터 생성
val jsonBody = "{\"name\": \"홍길동\"}"
// MockWebServer가 반환할 응답을 미리 설정 해준다.
mockWebServer.enqueue(MockResponse().setBody(jsonBody))
//✅ when : 테스트 진행
val result = myService.someMethod()
//✅ then : 검증 작업
StepVerifier.create(result)
.expectNext(jsonBody)
.verifyComplete()
//✅ 추가 : 요청에 대한 체크 방법
// MockWebServer가 받은 요청이 무엇인가 검증
val recordedRequest = mockWebServer.takeRequest()
assertEquals("GET", recordedRequest.method)
assertEquals(url, recordedRequest.path)
}
}
✅ 테스트 결과
references
https://www.baeldung.com/spring-mocking-webclient
https://www.devkuma.com/docs/mock-web-server/
'백엔드 > 테스트' 카테고리의 다른 글
[MockK] 코틀린을 위한 mocking library, 단위테스트 작성방법 (0) | 2023.06.07 |
---|---|
성능테스트 도구 JMeter란? 설치(MacOS, M1) (0) | 2023.06.03 |
[개선] 테스팅 빌드 속도 개선 (0) | 2023.01.09 |
[Testcode][통합] CategoryIntegrationTest (0) | 2023.01.09 |
[Testcode][슬라이스][Service] CategoryServiceTest (0) | 2023.01.09 |