DevelopmentTools/Java

Object Mother 패턴이란?

수짱수짱 2023. 12. 13. 01:40
해당 게시글은 "프로그래머스 데브코스 4기"의 팀 내 프로젝트 기록용으로 TECH BLOG에 직접 작성한 글입니다.

Object Mother패턴이란?

한 줄로 표현하면 “test Object를 만들어 주는 Class”이다.

테스트 데이터를 작성할 때 많은 예제 데이터가 필요한데 테스트 시점마다 새로운 데이터를 만드는 것이 비효율적인 task라고 생각이 들었고 이를 해결하기 위해 착안된 패턴이 “Object Mother 패턴"이다.

여러 테스트 클래스에서 유사한 데이터가 필요한 경우가 많아 테스트 시점에 standard fixtures를 만들 수 있는 factory object를 만드는 것이다.

 

장점 단점
  • 재사용성이 증가한다
  • 여러 테스트에 유사한 데이터를 사용하면 사용자들이 테스트 코드에 친숙해질 수 있다
  • 고정된 환경에서 테스트할 수 있음을 보장한다
  • 값이 변화하는 테스트에는 사용을 유의해야 한다
  • 데이터 클래스가 변경되면 Fixture도 변경이 되어야 한다

 

Fixture란 “고정되어 있는 물체”를 의미한다.

JUnit 테스트의 Fixture는 테스트 실행을 위한 베이스 라인으로 사용되는 객체들의 고정된 상태이다.

아래와 같은 예시가 반복적으로 사용되는 고정 객체가 필요한 경우이다

 

  • 게시글 생성할 때 필요한 유저 정보
  • 댓글을 생성할 때 필요한 유저 정보
  • 결제 할 때 필요한 유저 정보
  • 유저 정보 조회를 위해 필요한 유저 정보

적용 사례

public enum OrderFixture {

    BASE_ORDER(1L, List.of(
            new CreateOrderItem.Request(1L, "소고기", 30000, 2),
            new CreateOrderItem.Request(2L, "비싼 소고기", 20000, 1)
            ),
            50000,
            0,
            "컬리페이(국민은행)",
            "123456",
            "홍길동",
            "010-1234-****",
            "우리집",
            "문 앞",
            "공동현관 비밀번호(****)",
            "종이 포장재"),
            
    BASIC_ORDER(1L, List.of(
            new CreateOrderItem.Request(1L, "소고기", 30000, 2),
            new CreateOrderItem.Request(2L, "비싼 소고기", 20000, 1)
            ),
            50000,
            0,
            "컬리페이(국민은행)",
            "777123",
            "브라운",
            "010-1234-****",
            "우리집",
            "문 앞",
            "공동현관 비밀번호(****)",
            "종이 포장재");

    private final Long userId;
    private final List<CreateOrderItem.Request> orderItemRequests;
    private final int totalPrice;
    private final int totalDiscount;
    private final String payment;
    private final String payPassword;
    private final String receiver;
    private final String phoneNumber;
    private final String address;
    private final String receiveArea;
    private final String entranceInfo;
    private final String packaging;

    OrderFixture(Long userId, List<CreateOrderItem.Request> orderItemRequests, int totalPrice, int totalDiscount, String payment, String payPassword, String receiver, String phoneNumber, String address, String receiveArea, String entranceInfo, String packaging) {
        this.userId = userId;
        this.orderItemRequests = orderItemRequests;
        this.totalPrice = totalPrice;
        this.totalDiscount = totalDiscount;
        this.payment = payment;
        this.payPassword = payPassword;
        this.receiver = receiver;
        this.phoneNumber = phoneNumber;
        this.address = address;
        this.receiveArea = receiveArea;
        this.entranceInfo = entranceInfo;
        this.packaging = packaging;
    }
	
    ...
  
}

Order 테스트 데이터 객체가 필요할 때 enum을 이용해서 싱글톤 Fixture 객체를 생성했다.

 

	
    @Test
    @DisplayName("주문하면 주문 번호가 생성되고 실제 주문 금액이 계산된다.")
    void createOrder_test() {
        // given
        Order order = BASIC_ORDER.toEntity(); // 여기!
        User user = USER_FIXTURE.toEntity();
        given(orderRepository.save(any())).willReturn(order);
        given(passwordEncoder.matches(anyString(), anyString())).willReturn(true);

        // when
        CreateOrder.Response response = orderService.createOrder(user, BASIC_ORDER.toRequest()); // 여기!

        // then
        assertThat(response.orderNumber()).isNotNull();
        assertThat(response.totalPrice()).isEqualTo(order.getActualPayAmount());
    }

따라서, 실제 Order 도메인 테스트에서 Order order = new Order(…)로 긴 정보를 입력할 필요 없이 enum 싱글톤 객체를 Entity로 변환하여 불러오면 예시 데이터 입력없이 바로 사용 가능하다.

 

public enum ReviewFixture {
    REVIEW_FIXTURE(1L, "LA갈비", "고기가 너무 맛있어요!", false),
    SECRET_REVIEW_FIXTURE(1L, "LA갈비", "고기가 너무 맛있어요! 비밀이에요!!", true)
    ;

    private final Long productId;
    private final String productName;
    private final String content;
    private final boolean isSecret;

    ReviewFixture(Long productId, String productName, String content, boolean isSecret) {
        this.productId = productId;
        this.productName = productName;
        this.content = content;
        this.isSecret = isSecret;
    }

    public Review toEntity() {
        return new Review(UserFixture.USER_FIXTURE.toEntity(), ProductFixture.LA_GOGI.toEntity(), content, isSecret);
    }

    public CreateReview.Request toRequest() {
        return new CreateReview.Request(1L, productId, productName, content, isSecret);
    }
}

마찬가지로 Review 테스트 데이터가 필요할 때도 Fixture를 생성하여 테스트 데이터를 싱글톤으로 만들 수 있다.

 

private Review review;

    @BeforeEach
    void initReview() {
        review = REVIEW_FIXTURE.toEntity();
    }

그럼 실제로 테스트 코드에서 바로 Review의 참조 변수에 할당해 줄 수 있다.

 


Reference