[인프런 워밍업 스터디 클럽 2기_BE] 4주차 회고록 정리

[인프런 워밍업 스터디 클럽 2기_BE] 4주차 회고록 정리


Presentation Layer

외부 요청을 가장 먼저 받는 계층에서는 파라미터에 대한 최소한의 검증을 진행한다.

테스트 진행 방식

비즈니스 로직과 영속성 계층은 스프링을 띄워 테스트하고, Presentation Layer는 Mocking 기술(MockMVC)을 활용한다.

MockMVC란?

의존성이 복잡해 실제 테스트가 어려운 경우, 스프링 MVC의 동작을 가짜 객체로 재현할 수 있는 테스트 프레임워크

 

요구사항

  • 관리자 페이지에서 상품 등록 가능

  • 상품명, 타입, 상태, 가격 등 입력받음

 

Native Query 작성 이유

TDD를 염두에 두고 테스트 -> 구현 -> 리팩토링 습관화

서비스, 레포지토리 코드가 얇으면 비슷하지만 코드가 많아질수록 차이가 생기므로 사소한 부분도 테스트 작성 권장.

동시성 이슈가 있다면 UUID 등을 활용.

@Transactional

읽기 전용 설정 시, 조회만 가능하고 CUD는 제한됨. JPA는 1차 캐시에 스냅샷을 생성해 변경을 감지하지만, 읽기 전용 시 성능 이점이 있음.

CQRS (Command/Read 분리)

CUD와 R은 8:2 비율로 책임을 분리하고, 마스터(쓰기)/슬레이브(읽기) DB로 나누어 엔드포인트를 관리할 수 있다.

@EnableJpaAuditing
@Configuration
public class JpaAuditingConfig {
}

전역 설정을 클래스로 나누어 관리.

기본 생성자를 이용해 역직렬화를 진행하고, 컨트롤러 테스트 코드는 다음과 같이 작성:

mvc.perform(
        post("/api/v1/products/new")
                .content(objectMapper.writeValueAsString(request))
                .contentType(MediaType.APPLICATION_JSON)
        )
        .andDo(print())
        .andExpect(status().isOk());

규격화된 응답을 위해 ApiResponse 클래스를 사용해 공통 응답을 반환. 기존의 ResponseEntity와 차이점을 확인.

NotNull, NotEmpty, NotBlank 차이점

성격에 따라 검증 위치를 분리하는 것이 중요하며, 하위 레이어가 상위 레이어를 알아야 하는 상황을 피하기 위해 서비스용 DTO를 따로 작성하고, 컨트롤러에서 toServiceDTO로 변환해 처리.

다른 API의 의존성 문제를 피하기 위해 DTO를 통한 책임 분리 중요


요구사항

매출 통계에 대한 메일 전송을 하는 요구사항

        // stubbing
   when(mailSendClient.sendEmail(any(String.class), any(String.class), any(String.class), any(String.class)))
  .thenReturn(true);

String의 값의 어떤 것이든 좋다! 라고 해서 테스트를 진행하는 방법

TestDouble

  1. Dummy : 아무 것도 하지 않는 깡통 객체

  2. Fake : 단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체

  3. Stub : 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체 그외에는 X

  4. Spy : Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체 일부는 실제 객체처럼 동작시키고 일부만 Stubbing

  5. Mock : 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체

Stub vs Mock

  • 가장 큰 차이는 Sutb은 상태 검증,Mock은 행위검증이다.

꿀팁

Mail같은 긴 네트워크를 탈 서비스 일때 트랜잭션을 걸지 않는것이 좋다.


  • @Mock: 테스트에서 가짜 객체 만들어서 의존성 대체할 때 사용.

  • @Spy: 실제 객체 쓰되, 필요한 메서드만 Mocking 가능.

  • @InjectMocks: @Mock이나 @Spy로 만든 객체들 주입해서 테스트 대상 생성.

BDDMockito Test

  • BDDMockito도 있다.

    • Mockito와 동일하지만 BDD스타일의 기능만 따랐기때문에 모든 동작이 동일하다.

    • 평소에 BDDMockito를 사용하는게 좋을거같다!


1. 한 문단에 한 주제!

  • 케이스가 많아지면 복잡한 방법보단 간단히 나누는 게 좋고, @DisplayName은 추상화보다는 명확하게 써야 한다.

2. 완벽하게 제어하기

  • createOrder처럼 오버로딩된 건, 제어가 안 되면 상위 레벨로 올려서 테스트 환경을 만들어야 한다.

3. 테스트 환경의 독립성 보장

  • 다른 API를 가져다 쓰면 의존성 올라가서 테스트 독립성이 떨어짐. 환경이 깨지지 않게 보장 필요.

stock1.deductQuantity(1);
  • 이렇게 수량 바꾸면 테스트 깨짐. 실패는 when이나 then에서만 나와야 하고, 길어지면 추적 어려워짐.

  • 생성자 기반이 좋고, 순수 빌더나 생성자 방식 추천. 팩토리 메서드는 지양.

4. 테스트 간 독립성 보장

  • 테스트 간 자원 공유 안 하는 게 좋고, static으로 인스턴스 쓰지 않기. 필요하면 5번처럼 해라.

5. 한눈에 보이는 Test Fixture 구성

  • 원하는 상태로 고정시키는 객체들로 beforeAll, BeforeEach, AfterEach가 대표적.

  • Fixture 수정 시 모든 테스트에 영향 미치니 조심해서 사용.

언제 쓰면 될까?

  • 테스트 이해에 문제 없거나, 수정해도 영향 없을 때.

  • 필요한 구성요소만 넣고, 최소한으로 관리.

Test Fixture 클렌징

  • DeleteAll vs DeleteAllInBatch

    • deleteAll은 N개 반복으로 속도 느려짐, DeleteAllInBatch가 더 빠름.

    • deleteAll은 순서 상관없이 다 지워줘서 안전.

  • Repository delete 순서 조정

    • Product PK가 Order에 묶여 있어 참조 문제 발생. 순서 맞춰 삭제 필요.

@Transaction은 평소에 쓰고, Batch 통합 테스트는 상황에 따라 사용.

@ParameterizedTest

  • 케이스 확장 시 값이나 환경 바꾸고 싶을 때 사용. 변수 없이 어노테이션만으로 편리하게 사용 가능.

@DynamicTest

  • 시나리오 테스트 진행 시 사용. 서버 여러 번 실행돼 속도 느려지지만, 테스트 환경 맞춰 설정하면 OK.

public abstract class IntegrationTestSupport{}
  • Mock 사용 시 새로운 서버 구성 필요하므로 상위 클래스로 올리거나 테스트 환경 두 개로 나눠서 사용.

private 메서드는?

  • 테스트 안 해도 된다.

  • 객체를 분리하는걸 고려해야한다.!!

RestDocs

API 문서를 자동으로 생성하는 도구 테스트 코드와 연계해 실제 요청·응답을 기반으로 신뢰성 높은 문서를 만들어줌!


과제를 진행하며 given then when을 어떻게 나누고 Test Fixture을 어떤 방식으로 나누는지 고민을 많이했다.

강의에서 보던 코드를 나누는건 할만했지만 직접 나누려고 생각을 하다보니 조금 힘들었던 부분이 많았다. 또한 Mocking에 대한 정확한 이해가 되지 않았는데 개념들을 정리하다보니 글을 정리하는 순간순간 이렇게 쓰면되겠다! 라고 생각하게 됐다.

마지막 주차고 많이 밀렸기도 했고 힘든 부분도 많았지만 또 뒤돌아본다면, 나에게 돌아오는 실력들이 근육처럼 붙을꺼라 생각한다!

관련 코드 : https://silvercastle.notion.site/18-12a1dc39fd1e80c3914ac646daf52996?pvs=4

 

댓글을 작성해보세요.

채널톡 아이콘