2019. 4. 28. 02:21ㆍ[정리] 기능별 개념 정리/스프링 부트
MOCK 의 사전적 의미 : 모조품
Mocking 과 Spying
테스트할 때 원격 서버의 api 와 통신해봐야 할 수도 있다. 그런데 이렇게 되면 테스트 코드가 원격 서버에 의존적이다. 원격 서버와 통신해야하므로 테스트 코드의 성능도 문제가 된다. 또한 원격 서버가 다운되어 있을 때를 테스트하는 코드가 작성되야 할 수도 있다. 이런 경우 실제로 원격 서버를 다운시킬 수도 없는 노릇이다.이럴 때를 위해서 Spring boot 는 @MockBean과 @SpyBean을 제공한다. + MockRestServiceServer
@MockBean
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
@Autowired
private MyService myService;
@MockBean
private MyClient myClient;
@Test
public void exampleTest() {
given(myClient.call()).willReturn("hello");
String result = myService.whatRecieved();
assertThat(result).isEqualTo("I recieve hello");
}
}
가정
1. MyClient 는 원격 서버와 통신하는 클라이언트다.
2. MyService 는 MyClient 를 주입받아서 사용한다.
3. MyService 의 whatRecieved() 메소드는 원격 서버에서 받은 값을 "I recieve ~ " 으로 포맷팅해서 return 해준다.
@MockBean 의 동작 : MyService 가 MyClient 를 주입받을 때 @MockBean으로 만든 myClient 가 주입된다. 그리고 myClient 의 call() 이라는 메소드는 "hello"을 return 하도록 Mocking 해놨다. MyService 가 whatRecieved 를 호출하면 myClient 의 call 메소드가 연쇄 호출 될 것이고 myClient의 call 메소드가 호출 될 때 실제로 call 메소드를 호출하지 않고 mocking 된 결과 값인 hello 가 return 된다.
@SpyBean
MockBean 으로 등록된 객체는 빈 객체이기 때문에 만약 MockBean 으로 등록된 객체가 아무것도 mocking 하지 않으면 MockBean 객체의 메소드의 실행은 의미 없는 값을 반환하게 된다. ex) 만약 반환 타입이 String 일 경우 "No content" 가 반환된다.
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
@Autowired
private MyService myService;
@MockBean
private MyClient myClient;
@Test
public void exampleTest() {
String result = myService.whatRecieved();
assertThat(result).isEqualTo("I recieve hello");
}
}
위 코드의 myService.whatRecieved(); 결과는 "I recieve No content" 이므로 테스트는 실패한다.
@SpyBean의 동작은 Mocking 하지 않은 메소드에 대해서 기존의 제대로된 메소드를 실행시킨다.
@SpyBean은 기존의 객체에 있는 코드를 그대로 사용하면서 일부만 변경한다.
MockRestServiceServer
MockRestServiceServer 를 이용하면 가상의 가짜 서버를 만들고 request에 대한 return 정도를 할 수 있게 만들 수 있다. (호스트가 실제로 뜨는건 아니다. RestTemplate 을 생성할 때 테스트 코드시 이쪽으로 연결되도록 설정하는 것뿐인 듯.) RestTemplate 만 테스트할 수 있는 듯하다.
@RunWith(SpringRunner.class)
@RestClientTest(MyService.class)
public class RestClientTest {
@Autowired
private MyService myService;
@Autowired
private MockRestServiceServer mockServer;
@Before
public void setup(){
mockServer
.expect(requestTo("/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
}
@Test
public void test() throws Exception {
String greeting = myService.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
WireMock
WebClient 까지 테스트 가능한 Mock 서버를 만들 수 있다.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MainApplication.class)
@AutoConfigureWireMock(port = 9999)
public class SymbolizerTemplateTest {
@Autowired
private WebClient myWebClient;
// Thread safe
private static ObjectMapper objectMapper = new ObjectMapper();
@Before
public void setUp(){
createMockServer();
}
private void createMockServer(){
configureFor("localhost",9999);
stubFor(post(urlPathMatching("/api/user/(.*)"))
.willReturn(aResponse()
.withStatus(200)
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
.withBody(dummyUser())));
}
private String dummyUser(){
User dummyUser = new User("kok202", "Hello world");
try{ return objectMapper.writeValueAsString(dummyUser);
}catch (Exception e){ return ""; }
}
@Test
public void test() throws Exception{
...
}
}
아래의 두 포스팅을 재구성하였습니다.
https://kok202.tistory.com/142?category=795332
https://kok202.tistory.com/143?category=795332
'[정리] 기능별 개념 정리 > 스프링 부트' 카테고리의 다른 글
스프링 부트 Cache (0) | 2019.05.27 |
---|---|
PSA (추상화 계층) (0) | 2019.05.01 |
스프링 AOP, Transaction (0) | 2019.04.21 |
Spring boot's NoSQL (0) | 2019.04.21 |
Spring boot's RestTemplate, WebClient (0) | 2019.04.21 |