Cypress e2e 테스트에서 Zustand 상태에 접근하는 방법

이형준·2024년 1월 14일
1

트러블슈팅

목록 보기
6/7
post-thumbnail

Cypress 는 모던 웹 어플리케이션 테스트를 도와주는 도구이다. 최근에 프론트엔드 테스트에 관심이 생겨 이것저것 공부하던 중 알게 된 툴인데, 테스팅에 관련된 다양한 기능들을 제공해준다.

특히 e2e 테스트라고 부르는 엔드 투 엔드 테스트에 매우 강력한 모습을 보여주는데, 우선 직관적인 API를 제공하여 가독성 좋은 테스트 코드를 작성하기 용이하다. 간단한 예시를 들면,

describe("간단한 테스트", () => {
  it("페이지를 방문하면, 버튼이 렌더링되어야 한다.", () => {
    cy.visit("/");
    cy.get("[data-cy=button]").should("exist");
  });
});

보다시피 should(---) 와 같은 직관적인 문법으로, 테스트 코드에 익숙하지 않은 개발자라도 코드의 흐름과 목적을 파악하기 용이하다.

또한, 실제 웹 브라우저에서 테스트 코드를 실행한다! 이는 매우 큰 장점인데, 테스트의 흐름을 가시적으로 확인할 수 있다. 이와 같이 실제 유저 환경에서 테스트를 진행할 수 있다는 건 매우 큰 장점!

  • Cypress를 이용하여 e2e 테스트를 진행하는 모습. 실제 브라우저를 이용하여 테스트를 진행하고, 이를 확인할 수 있는 모습이 인상깊다.

전역 상태들에 대한 접근?

신나게 툴에 대해 공부하고, 개인 프로젝트에서 사용해보던 중, 문득 궁금한 점이 생겼다.
상태 관리 라이브러리를 이용하여 전역으로 관리되는 state들에 대한 테스트는 어떻게 접근하여 진행해야 할까? 공식 문서를 통해 답을 찾아보려 했으나..

아쉽게도 내가 주로 사용하는 상태 관리 라이브러리, Zustand 에 대한 가이드는 어떠한 공식 문서에서도, 국내/외에서 작성된 글에서도 찾을 수 없었다 😂

하지만 둘 중 하나를 포기할 수는 없는 노릇! 어떻게든 내가 답을 찾아보기로 했다.


힌트 발견

Cypress 공식 문서를 살펴보다 보면, Redux의 상태에 접근하여 테스트를 진행하는 방법이 소개되어 있다.

비록 내가 원하는 Zustand를 이용하는 방법은 아니지만, 한번 확인해보자.

const store = createStore(reducer)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

// expose store when run in Cypress
if (window.Cypress) {
  window.store = store
}
it('has expected state on load', () => {
  cy.visit('/')
  cy.window().its('store').then(store => {
    // manipulate the store reference
  })
})

Redux 의 문법적인 부분은 배제하고, 어떻게 상태에 접근하여 테스트를 진행하는 지에 주목해보자.

  • Cypress 테스트 환경일 시, window.storeRedux Store을 할당한다.
  • 이후, 테스트 시 cy.windex().its('store')...과 같이 할당해 놓은 Store에 접근한다.

위 코드는 물론 Redux 환경을 위한 예제이지만, 적어도 이 접근법은 Zustand에도 유효할 수 있지 않을까? 하는 생각이 들었다.


문제 해결

위의 가이드와 유사하게, 프로젝트의 루트 컴포넌트(App.js)에 다음과 같은 코드를 추가하여 Cypress 테스트 시에 Zustand Storewindow.store 에 할당하도록 해 보았다.

처음에는 단순히 위 코드를 복붙해서 넣었었는데, 생각해보니 App.js 도 컴포넌트인지라 잘 동작하지 않았다. useEffect 훅을 이용하여 해결!

useEffect(() => {
        if (window.Cypress) {
            window.store = useStore;
        }
    }, []);
  • 위 코드를 App.js 에 추가하고,
describe("행맨 게임 작동 테스트", () => {
    it("사용자는 게임 시작 버튼을 통해 게임을 시작할 수 있다.", () => {
        // given - 페이지에 접근한다
        cy.visit("/");
        // when - 게임 시작 버튼을 클릭한다
        cy.get("[data-cy=startButton]").click();
        // then - 게임이 시작된다
        cy.get("[data-cy=gameStartModal]").should("not.exist");
        cy.window()
            .its("store")
            .invoke("getState")
            .its("gameStartMessage")
            .should("not.eq", "None");
    });
});
  • 간단한 테스트 코드를 실행하여 store에 접근을 시도해보았다.

결과는?

헉 😀 ZustandStore에 잘 접근하여, 성공적으로 테스트가 통과됨을 확인할 수 있었다. 혹시 몰라서 콘솔에서 state 를 확인해보기도 했는데, 잘 접근하여 로그가 찍히는 모습


결론

Cypress 를 이용한 e2e 테스트 시, Zustand 로 관리되는 전역 상태에 접근하여 테스트를 진행하고 싶은 경우,

  1. useEffect 훅을 이용하여(Cypress 를 통한 실행 시에만) window.storeZustand Store를 할당한다.

  2. cy.window().its("store").invoke("getState").its("${state}")
    와 같이 상태에 접근할수 있다.

profile
저의 미약한 재능이 세상을 바꿀 수 있을 거라 믿습니다.

2개의 댓글

comment-user-thumbnail
2024년 1월 16일

열심히 하는 모습이 보기 좋습니다!
상태 메시지도 바뀐 것 같네요. 화이팅입니다!

1개의 답글