<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Springboottest on kastori</title><link>http://blog.kastori.dev/tags/springboottest/</link><description>Recent content in Springboottest on kastori</description><generator>Hugo -- gohugo.io</generator><language>ko-kr</language><lastBuildDate>Tue, 19 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://blog.kastori.dev/tags/springboottest/index.xml" rel="self" type="application/rss+xml"/><item><title>[Spring 완전 정복 #11] Spring 테스트 전략 — 단위·슬라이스·통합 테스트를 언제 어떻게 쓸까</title><link>http://blog.kastori.dev/tech/2026-05-19-spring-11-testing/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>http://blog.kastori.dev/tech/2026-05-19-spring-11-testing/</guid><description>&lt;h2 id="테스트를-왜-계층으로-나누는가"&gt;&lt;a href="#%ed%85%8c%ec%8a%a4%ed%8a%b8%eb%a5%bc-%ec%99%9c-%ea%b3%84%ec%b8%b5%ec%9c%bc%eb%a1%9c-%eb%82%98%eb%88%84%eb%8a%94%ea%b0%80" class="header-anchor"&gt;&lt;/a&gt;테스트를 왜 계층으로 나누는가
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;@SpringBootTest&lt;/code&gt;를 쓰면 전체 Spring Context를 로드하므로 모든 것을 테스트할 수 있다. 그런데 왜 단위 테스트, 슬라이스 테스트를 따로 쓸까?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;속도&lt;/strong&gt; 때문이다. &lt;code&gt;@SpringBootTest&lt;/code&gt;는 전체 Context를 띄우므로 느리다. 단순 비즈니스 로직 검증에 매번 10초씩 기다리는 것은 비효율적이다. 테스트 종류를 목적에 맞게 선택하면 빠르고 명확한 테스트를 작성할 수 있다.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;단위 테스트 : 빠름 — 비즈니스 로직
@WebMvcTest : 빠름 — API 계약, Validation, HTTP 상태코드
@DataJpaTest : 빠름 — 쿼리 메서드, 커스텀 쿼리
@SpringBootTest : 느림 — 전체 플로우 통합 검증
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="단위-테스트--junit5--mockito"&gt;&lt;a href="#%eb%8b%a8%ec%9c%84-%ed%85%8c%ec%8a%a4%ed%8a%b8--junit5--mockito" class="header-anchor"&gt;&lt;/a&gt;단위 테스트 — JUnit5 + Mockito
&lt;/h2&gt;&lt;p&gt;Spring Context 없이 순수 Java로 테스트한다. 가장 빠르고 의존성이 없다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@ExtendWith&lt;/span&gt;(MockitoExtension.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderServiceTest&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Mock&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; OrderRepository orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Mock&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; PaymentService paymentService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@InjectMocks&lt;/span&gt; &lt;span style="color:#75715e"&gt;// @Mock 객체들을 OrderService에 자동 주입&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; OrderService orderService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;주문_생성_성공&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// given&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; OrderRequest request &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; OrderRequest(1L, 2, 10000);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order mockOrder &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Order.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;(1L).&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(OrderStatus.&lt;span style="color:#a6e22e"&gt;PENDING&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; given(orderRepository.&lt;span style="color:#a6e22e"&gt;save&lt;/span&gt;(any(Order.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;))).&lt;span style="color:#a6e22e"&gt;willReturn&lt;/span&gt;(mockOrder);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// when&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; OrderDto result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; orderService.&lt;span style="color:#a6e22e"&gt;createOrder&lt;/span&gt;(request);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(result.&lt;span style="color:#a6e22e"&gt;getId&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;isEqualTo&lt;/span&gt;(1L);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; verify(orderRepository, times(1)).&lt;span style="color:#a6e22e"&gt;save&lt;/span&gt;(any(Order.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;재고_부족_시_예외_발생&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; given(productRepository.&lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(anyLong()))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;willReturn&lt;/span&gt;(Optional.&lt;span style="color:#a6e22e"&gt;of&lt;/span&gt;(Product.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;stock&lt;/span&gt;(0).&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;()));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThatThrownBy(() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; orderService.&lt;span style="color:#a6e22e"&gt;createOrder&lt;/span&gt;(request))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;isInstanceOf&lt;/span&gt;(InsufficientStockException.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;ArgumentCaptor&lt;/strong&gt;로 메서드에 전달된 인자를 검증할 수 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ArgumentCaptor&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Order&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; captor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ArgumentCaptor.&lt;span style="color:#a6e22e"&gt;forClass&lt;/span&gt;(Order.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;verify(orderRepository).&lt;span style="color:#a6e22e"&gt;save&lt;/span&gt;(captor.&lt;span style="color:#a6e22e"&gt;capture&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(captor.&lt;span style="color:#a6e22e"&gt;getValue&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;getStatus&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;isEqualTo&lt;/span&gt;(OrderStatus.&lt;span style="color:#a6e22e"&gt;PENDING&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="webmvctest--controller-레이어만-테스트"&gt;&lt;a href="#webmvctest--controller-%eb%a0%88%ec%9d%b4%ec%96%b4%eb%a7%8c-%ed%85%8c%ec%8a%a4%ed%8a%b8" class="header-anchor"&gt;&lt;/a&gt;@WebMvcTest — Controller 레이어만 테스트
&lt;/h2&gt;&lt;p&gt;Controller, Filter, &lt;code&gt;@ControllerAdvice&lt;/code&gt;만 로드한다. Service, Repository는 &lt;code&gt;@MockBean&lt;/code&gt;으로 대체한다. MockMvc로 실제 HTTP 요청처럼 테스트한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@WebMvcTest&lt;/span&gt;(OrderController.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderControllerTest&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; MockMvc mockMvc;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@MockBean&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Spring Context에 Mock Bean 등록&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; OrderService orderService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; ObjectMapper objectMapper;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;주문_생성_API_테스트&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// given&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CreateOrderRequest request &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; CreateOrderRequest(1L, 2);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; OrderDto response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; OrderDto.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;(1L).&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;PENDING&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; given(orderService.&lt;span style="color:#a6e22e"&gt;createOrder&lt;/span&gt;(any())).&lt;span style="color:#a6e22e"&gt;willReturn&lt;/span&gt;(response);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// when &amp;amp; then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mockMvc.&lt;span style="color:#a6e22e"&gt;perform&lt;/span&gt;(post(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/orders&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;contentType&lt;/span&gt;(MediaType.&lt;span style="color:#a6e22e"&gt;APPLICATION_JSON&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;(objectMapper.&lt;span style="color:#a6e22e"&gt;writeValueAsString&lt;/span&gt;(request)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;andExpect&lt;/span&gt;(status().&lt;span style="color:#a6e22e"&gt;isCreated&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;andExpect&lt;/span&gt;(jsonPath(&lt;span style="color:#e6db74"&gt;&amp;#34;$.id&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;(1L))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;andExpect&lt;/span&gt;(jsonPath(&lt;span style="color:#e6db74"&gt;&amp;#34;$.status&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;PENDING&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;andDo&lt;/span&gt;(print());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Validation_실패_시_400_반환&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CreateOrderRequest invalidRequest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; CreateOrderRequest(&lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;, 2);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mockMvc.&lt;span style="color:#a6e22e"&gt;perform&lt;/span&gt;(post(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/orders&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;contentType&lt;/span&gt;(MediaType.&lt;span style="color:#a6e22e"&gt;APPLICATION_JSON&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;(objectMapper.&lt;span style="color:#a6e22e"&gt;writeValueAsString&lt;/span&gt;(invalidRequest)))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;andExpect&lt;/span&gt;(status().&lt;span style="color:#a6e22e"&gt;isBadRequest&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Spring Security가 설정된 프로젝트에서는 인증 설정이 필요하다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@WebMvcTest&lt;/span&gt;(OrderController.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@WithMockUser&lt;/span&gt;(roles &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;USER&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// 인증된 사용자로 테스트&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderControllerTest&lt;/span&gt; { ... }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;@MockBean&lt;/code&gt; vs &lt;code&gt;@Mock&lt;/code&gt;의 차이&lt;/strong&gt;: &lt;code&gt;@MockBean&lt;/code&gt;은 Spring Context에 Mock을 올린다(슬라이스 테스트에서 사용). &lt;code&gt;@Mock&lt;/code&gt;은 Spring 없이 Mockito만으로 사용하는 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="datajpatest--jpa-레이어만-테스트"&gt;&lt;a href="#datajpatest--jpa-%eb%a0%88%ec%9d%b4%ec%96%b4%eb%a7%8c-%ed%85%8c%ec%8a%a4%ed%8a%b8" class="header-anchor"&gt;&lt;/a&gt;@DataJpaTest — JPA 레이어만 테스트
&lt;/h2&gt;&lt;p&gt;JPA 관련 Bean만 로드한다. 기본적으로 인메모리 H2 DB를 사용하고, 각 테스트 후 자동 롤백한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@DataJpaTest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderRepositoryTest&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; OrderRepository orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; TestEntityManager em;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;주문_저장_및_조회&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// given&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Member member &lt;span style="color:#f92672"&gt;=&lt;/span&gt; em.&lt;span style="color:#a6e22e"&gt;persist&lt;/span&gt;(Member.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Order.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;member&lt;/span&gt;(member).&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(OrderStatus.&lt;span style="color:#a6e22e"&gt;PENDING&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// when&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order saved &lt;span style="color:#f92672"&gt;=&lt;/span&gt; orderRepository.&lt;span style="color:#a6e22e"&gt;save&lt;/span&gt;(order);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; em.&lt;span style="color:#a6e22e"&gt;flush&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// 1차 캐시 → DB 즉시 반영&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; em.&lt;span style="color:#a6e22e"&gt;clear&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// 1차 캐시 비우기 → 실제 DB 조회 강제&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order found &lt;span style="color:#f92672"&gt;=&lt;/span&gt; orderRepository.&lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(saved.&lt;span style="color:#a6e22e"&gt;getId&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(found.&lt;span style="color:#a6e22e"&gt;getStatus&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;isEqualTo&lt;/span&gt;(OrderStatus.&lt;span style="color:#a6e22e"&gt;PENDING&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;em.flush()&lt;/code&gt; + &lt;code&gt;em.clear()&lt;/code&gt; 패턴이 중요하다. flush 없이 findById를 하면 1차 캐시에서 반환되어 DB에 실제로 저장됐는지 확인할 수 없다. flush로 DB에 반영하고 clear로 캐시를 비운 뒤 다시 조회해야 한다.&lt;/p&gt;
&lt;p&gt;실제 DB로 테스트하려면:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@DataJpaTest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@AutoConfigureTestDatabase&lt;/span&gt;(replace &lt;span style="color:#f92672"&gt;=&lt;/span&gt; AutoConfigureTestDatabase.&lt;span style="color:#a6e22e"&gt;Replace&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NONE&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@ActiveProfiles&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderRepositoryTest&lt;/span&gt; { ... }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="springboottest--통합-테스트"&gt;&lt;a href="#springboottest--%ed%86%b5%ed%95%a9-%ed%85%8c%ec%8a%a4%ed%8a%b8" class="header-anchor"&gt;&lt;/a&gt;@SpringBootTest — 통합 테스트
&lt;/h2&gt;&lt;p&gt;전체 Spring Context를 로드한다. 실제 DB와 함께 통합 테스트에 사용한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@SpringBootTest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Transactional&lt;/span&gt; &lt;span style="color:#75715e"&gt;// 테스트 후 자동 롤백&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderServiceIntegrationTest&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; OrderService orderService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; OrderRepository orderRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;주문_생성_통합_테스트&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CreateOrderRequest request &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; CreateOrderRequest(1L, 2);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; OrderDto result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; orderService.&lt;span style="color:#a6e22e"&gt;createOrder&lt;/span&gt;(request);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Order savedOrder &lt;span style="color:#f92672"&gt;=&lt;/span&gt; orderRepository.&lt;span style="color:#a6e22e"&gt;findById&lt;/span&gt;(result.&lt;span style="color:#a6e22e"&gt;getId&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(savedOrder.&lt;span style="color:#a6e22e"&gt;getStatus&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;isEqualTo&lt;/span&gt;(OrderStatus.&lt;span style="color:#a6e22e"&gt;PENDING&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MockMvc와 함께 전체 API 플로우를 테스트할 수 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@SpringBootTest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@AutoConfigureMockMvc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OrderApiIntegrationTest&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; MockMvc mockMvc;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;전체_주문_플로우_테스트&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mockMvc.&lt;span style="color:#a6e22e"&gt;perform&lt;/span&gt;(post(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/orders&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;header&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; getToken())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;contentType&lt;/span&gt;(MediaType.&lt;span style="color:#a6e22e"&gt;APPLICATION_JSON&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;(requestJson))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;andExpect&lt;/span&gt;(status().&lt;span style="color:#a6e22e"&gt;isCreated&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="4가지-비교"&gt;&lt;a href="#4%ea%b0%80%ec%a7%80-%eb%b9%84%ea%b5%90" class="header-anchor"&gt;&lt;/a&gt;4가지 비교
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;구분&lt;/th&gt;
 &lt;th&gt;속도&lt;/th&gt;
 &lt;th&gt;범위&lt;/th&gt;
 &lt;th&gt;목적&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;JUnit5 + Mockito&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;가장 빠름&lt;/td&gt;
 &lt;td&gt;클래스 단위&lt;/td&gt;
 &lt;td&gt;비즈니스 로직&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;@WebMvcTest&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;빠름&lt;/td&gt;
 &lt;td&gt;Controller 레이어&lt;/td&gt;
 &lt;td&gt;API 계약, Validation, 상태코드&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;@DataJpaTest&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;빠름&lt;/td&gt;
 &lt;td&gt;JPA 레이어&lt;/td&gt;
 &lt;td&gt;쿼리 메서드, 커스텀 쿼리&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;@SpringBootTest&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;느림&lt;/td&gt;
 &lt;td&gt;전체 컨텍스트&lt;/td&gt;
 &lt;td&gt;E2E 플로우, 통합 검증&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="자주-쓰는-assertj"&gt;&lt;a href="#%ec%9e%90%ec%a3%bc-%ec%93%b0%eb%8a%94-assertj" class="header-anchor"&gt;&lt;/a&gt;자주 쓰는 AssertJ
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result).&lt;span style="color:#a6e22e"&gt;isEqualTo&lt;/span&gt;(expected);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(list).&lt;span style="color:#a6e22e"&gt;hasSize&lt;/span&gt;(3).&lt;span style="color:#a6e22e"&gt;contains&lt;/span&gt;(element);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(list).&lt;span style="color:#a6e22e"&gt;isEmpty&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThatThrownBy(() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; service.&lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;isInstanceOf&lt;/span&gt;(SomeException.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;hasMessageContaining&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;에러 메시지&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(order)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;extracting&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;containsExactly&lt;/span&gt;(OrderStatus.&lt;span style="color:#a6e22e"&gt;PENDING&lt;/span&gt;, 10000);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="마치며"&gt;&lt;a href="#%eb%a7%88%ec%b9%98%eb%a9%b0" class="header-anchor"&gt;&lt;/a&gt;마치며
&lt;/h2&gt;&lt;p&gt;테스트는 단계별로 목적에 맞게 작성하는 것이 효율적이다. 비즈니스 로직은 빠른 단위 테스트로, Controller 계층은 &lt;code&gt;@WebMvcTest&lt;/code&gt;로, JPA 쿼리는 &lt;code&gt;@DataJpaTest&lt;/code&gt;로 검증한다. &lt;code&gt;@SpringBootTest&lt;/code&gt;는 전체 플로우 통합 검증에만 사용한다. 이렇게 하면 테스트 속도를 유지하면서 신뢰성 있는 테스트 스위트를 구성할 수 있다.&lt;/p&gt;</description></item></channel></rss>