강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

꽃갈피님의 프로필 이미지
꽃갈피

작성한 질문수

실전 프론트엔드 테스트 시작하기

장바구니 테스트 코드 작성에 대한 질문입니다.

작성

·

12

0

강의를 들으며 장바구니 페이지에 대해 Cypress로 테스트 코드를 작성하다가 궁금한 점이 몇가지 있어서 질문을 작성합니다 :)

  1. 장바구니 페이지에서 '장바구니의 상품을 삭제한 후 장바구니에 담겨있는 총 수량과 가격이 변경된다.'라고 테스트 시나리오를 작성했는데 이런 걸 e2e로 테스트 하는 게 맞는지 잘 모르겠습니다.
    이런 부분은 unit 테스트로 넘겨야 하는 부분일까요?
    e2e테스트는 어떤 걸 중심으로 하면 좋을지 잘 모르겠습니다. 그냥 제가 필요하다고 생각되는 게 있으면 진행을 하면 되는 걸까요?
    저도 모르게 저 대신 클릭 등을 수행하고 값을 확인하는 용도로 다루게 되는 것 같습니다.

  2. 그래도 일단 1번 질문에서 작성한 테스트 시나리오에 대한 코드를 직접 작성해봤습니다.
    이 때, 장바구니 페이지에서 장바구니 아이템 데이터가 getServerSideProps를 통해 주입되고 있더라구요.


    이 상황에서 API 모킹을 어떻게 해야할지 몰라서 Claude Sonnet 4.6이랑 구글링을 통해 몇가지 코드를 작성해봤습니다.
    단순하게 테스트 코드가 작성되지 않고 다소 복잡하게 작성이 되는 것 같아서 뭔가 이 방향이 맞지 않는 것 같다는 생각이 들었습니다. 그래서 테스트 시나리오 자체를 제가 잘못 생각한 것 같다는 생각이 들기도 했습니다.
    SSR 환경에서 어떤 코드로 작성을 하는 게 맞는 방식인지 알 수가 없어서 한 번 확인해봐주시면 감사하겠습니다 :)

    a. nock + intercept 사용
    - SSR은 서버 프로세스에서 진행되기 때문에 intercept 할 수 없다고 하여 nock을 사용했습니다. 코드는 해당 링크를 참고 했습니다.
    - 그리고 router.replace를 통해 데이터를 refetch 될 때도 백엔드 api를 intercept를 할 수 없다고 해서 '/_next/data/**/*.json'를 intercept 했습니다.
    - 개인적으로 아래 코드는 'nock'과 'intercept'를 혼용해서 쓰다보니 장기적으로 봤을 때 유지보수성 측면에서 좋지 않은 코드라고 느꼈습니다.

       it.only('장바구니의 상품을 삭제한 후 총 수량과 가격이 변경된다', () => {
            // 1) 초기 로드용 nock (SSR - 서버사이드 fetch)
            cy.task('nock', {
              hostname: Cypress.env('API_URL'),
              method: 'GET',
              path: '/carts',
              statusCode: 200,
              body: FIVE_CART_ITEMS,
            });
    
            // 2) router.replace 후 재조회용 intercept (클라이언트사이드 fetch)
            cy.intercept('GET', '/_next/data/**/cart.json', {
              body: {
                pageProps: { carts: FOUR_CART_ITEMS_AFTER_DELETE },
                __N_SSP: true,
              },
            }).as('refetch');
    
            // 삭제 API mock
            cy.intercept('POST', '/api/cart', {
              statusCode: 200,
              body: { data: { name: '4' } },
            }).as('deleteCart');
    
            cy.visit('/cart');
    
            // action
            cy.getByCy('cart-item-delete-button').first().click();
            cy.wait('@deleteCart');
            cy.wait('@refetch'); // 페이지 갱신 대기
    
            // assertion
            cy.contains('Unbranded Rubber Chair').should('not.exist');
            cy.getByCy('cart-item').should('have.length', 4);
            cy.getByCy('cart-item-total-amount').should('have.text', '4');
            cy.getByCy('cart-item-total-price').should('have.text', '836.00');
        });

    b. intercept 사용

    • 그래서 초기 로드에도 intercept를 사용했습니다.

        it.only('장바구니의 상품을 삭제한 후 총 수량과 가격이 변경된다', () => {
            // 1) 초기 로드용 (SSR - 서버사이드 fetch)
            cy.intercept('GET', '/_next/data/**/*.json', {
                body: {
                  pageProps: { carts: FIVE_CART_ITEMS },
                  __N_SSP: true,
                },
              });
    
            // 2) router.replace 후 재조회용 intercept (클라이언트사이드 fetch)
            cy.intercept('GET', '/_next/data/**/*.json', {
              body: {
                pageProps: { carts: FOUR_CART_ITEMS_AFTER_DELETE },
                __N_SSP: true,
              },
            }).as('refetch');
    
    
            // 삭제 API mock
            cy.intercept('POST', '/api/cart', {
              statusCode: 200,
              body: { data: { name: '4' } },
            }).as('deleteCart');
    
            cy.visit('/cart');
    
            // action
            cy.getByCy('cart-item-delete-button').first().click();
            cy.wait('@deleteCart');
            cy.wait('@refetch'); // 페이지 갱신 대기
    
            // assertion
            cy.contains('Unbranded Rubber Chair').should('not.exist');
            cy.getByCy('cart-item').should('have.length', 4);
            cy.getByCy('cart-item-total-amount').should('have.text', '4');
            cy.getByCy('cart-item-total-price').should('have.text', '836.00');
        });

    c. API 모킹을 사용하지 않기
    - ssr 환경에서 delete 테스트를 위해 api 모킹을 사용하는 게 오히려 복잡하게 느껴져서 API 모킹을 사용하지 않고 코드를 작성하는 방식을 생각했습니다.
    - 대신에 테스트 할 때마다 수량과 가격이 변경될 거라고 생각해서 수량과 가격을 캡쳐하는 코드를 추가했습니다.

        it.only('장바구니의 상품을 추가한 뒤 삭제하면 해당 상품이 사라진다', () => {
            const cartItem = {
                "id": "24",
                "name": "Practical Frozen Pants",
                "price": "607.00",
                "imageUrl": "https://cdn.pixabay.com/user/2023/05/21/19-38-51-804_250x250.jpg"
            }
            // prepare: API로 상품 직접 추가
            cy.request('POST', `${Cypress.env('API_URL')}/carts`,   cartItem).then(({ body }) => {
        
                cy.visit('/cart');
    
                // 삭제 전 수량과 가격 캡처
                cy.getByCy('cart-item-total-amount').invoke('text').then((beforeAmount) => {
                    cy.getByCy('cart-item-total-price').invoke('text').then((beforePrice) => {
    
                        // action: 방금 추가한 항목 삭제
                        cy.contains('Practical Frozen Pants')
                            .closest('[data-cy="cart-item"]')
                            .find('[data-cy="cart-item-delete-button"]')
                            .click();
    
                        // assertion
                        cy.contains('Practical Frozen Pants').should('not.exist');
                        cy.getByCy('cart-item-total-amount').should('have.text', String(Number(beforeAmount) - 1));
                        cy.getByCy('cart-item-total-price').should('have.text', (Number(beforePrice) - cartItem.price).toFixed(2));
                    });
                });
            });
        });
    
  3. 일반적으로 delete에 대해 테스트할 때 어떤 식으로 하는지 궁금합니다. 삭제 성공 여부 / 해당 데이터가 보이지 않는지 체크 정도만 하고 삭제가 진행됨으로써 총 아이템 개수 수량이 변경되는 것에 대해서는 테스트 코드로 굳이 짚고 넘어가지 않나요?

     

  4. 실무에서 테스트 데이터는 보통 어떻게 관리하나요? 전부 api 모킹으로 테스트 해야하는 건지, 아니면 테스트용 DB를 따로 생성해서 거기서 테스트를 진행해야하는 건지 궁금합니다.
    아니면 상황에 따라서 api 모킹을 해야할 때가 있고, DB를 연결해서 테스트 해야할 때가 있나요?

한번에 질문이 다소 많아서 죄송합니다 ㅠㅠ 완강하고 며칠동안 혼자서 공부해보다가 제대로 제가 하고 있는 건지 모르겠어서 여쭤봅니다 ㅠㅠ
긴 글 읽어주셔서 감사합니다!!

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.
꽃갈피님, 질문 감사드립니다! 장바구니 테스트 코드 작성에 관한 문제를 해결해 드리겠습니다.

  1. e2e 테스트와 unit 테스트의 차이에 대해 설명 드리면, e2e(end-to-end) 테스트는 사용자가 애플리케이션을 실제로 사용하는 방식과 유사하게 전체적인 흐름을 테스트하는 것입니다. 장바구니 상품 삭제 후 총 수량과 가격이 변경되는 것은 사용자 입장에서 중요한 시나리오이므로 e2e 테스트로 구현하셔도 좋습니다. 반면, 개별적 로직의 정확성을 검증하는 경우는 unit 테스트가 적합합니다.

  2. SSR 환경에서의 API 모킹에 대한 접근법:

a. nock + intercept 방법: SSR의 경우 서버 측에서 이루어지기 때문에 nock을 사용하는 것이 맞습니다. 다만, interceptnock을 혼용 사용 시 코드의 복잡성과 유지보수성을 고려해야 합니다.

b. intercept 사용: 서버와 클라이언트 모두 intercept를 사용하여 테스트 가능하다면 코드 복잡성을 줄이는 데 유리합니다.

c. API 모킹을 사용하지 않기: 직접 실제 API를 호출하여 작업을 수행하는 것도 가능하지만, 테스트 환경의 변화를 예측하기 어려워질 수 있습니다.

  1. 삭제 테스트 일반적 접근: 삭제 자체가 성공했는지, UI 상에서 해당 항목이 사라졌는지를 테스트하는 것은 중요합니다. 삭제 후 아이템 총 개수와 가격 변경 검사는 기능의 완전성을 보장하는 차원에서 추가로 검증할 수 있습니다.

  2. 테스트 데이터 관리: 상황에 따라 다를 수 있습니다. API 모킹을 통해 외부 요인에 영향을 받지 않는 테스트를 구현할 수 있고, 테스트용 DB를 사용하여 실제 데이터 환경에 가까운 테스트를 실행하기도 합니다. 실제 데이터베이스 사용 시 더 현실적인 시나리오를 테스트할 수 있지만, 데이터가 변경될 수 있다는 점에서 관리가 필요합니다.

질문에 답해드린 내용이 꽃갈피님께 도움이 되었길 바랍니다. 테스트 작성하면서 어려운 부분이 있다면 언제든지 질문해 주세요. 감사합니다!

꽃갈피님의 프로필 이미지
꽃갈피

작성한 질문수

질문하기