두번째도 어려운 React testing

개발자 왜?전·2020년 11월 4일
0

test가 뭐에요?

목록 보기
2/2

나의 React App을 테스트 한 것이 아니라, jest와 enzyme을 테스트 한 게 아닐까?


저의 App은 Next js을 기반으로 redux로 상태관리를 하며 redux-saga를 통해 side-effects를 처리했습니다. 또한 styled-component와 SCSS를 사용하여 style을 처리했습니다.





Jest와 Enzyme를 열심히 테스트했다.


콘솔창의 pass를 본 나는 호기롭게 다음 작업을 구상했다.

expectsimulate를 통한 테스트. 콘솔창에 뜬 무서운 빨간 글씨..

시작이다.



mount와 shallow의 차이점


먼저 기본이 부족했다. 그저 pass 콘솔창을 보기위해 따라쳤기에 이런 기본적인 차이점도 확인하지 않고 성급히 테스트했다.

검색을 해보니 차이점은 shallow 함수는 컴포넌트를 자식 컴포넌트와 분리하여 테스트를 하고, mount 함수는 깊은 자식 컴포넌트 까지 테스트를 한다는 것이다.

redux 테스트를 하는 프로젝트는 <Provider>로 감싸져 테스트가 진행되어야 한다. 따라서 mount함수로 진행해야 한다.

후에 .html().debug()함수를 알게되어 확인해보니 shallow함수는 정말 자식 컴포넌트를 단순히 <Component />로 처리하고 있었다.



window.matchMedia is not a function


mount함수를 사용하니 콘솔창에 matchMedia는 function이 아니라고 뜬다. 내가 코드를 친적이 없는데 matchMedia가 function이 아니라니..

나는 사용하지 않았다고 생각한 곳에서 콘솔이 떳지만 검색결과 쉽게 해결 할 수 있었다.

matchMedia.mock 파일 생성

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(), // deprecated
    removeListener: jest.fn(), // deprecated
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

파일을 test파일에 import 하면 된다.



useSelctor를 쓰고 싶어요!


나의 테스트 컴포넌트는 useSelectorme라는 객체를 가져와서 데이터를 출력한다. 그런데 테스트를 rudecer, action을 나눠서 하지 않고 통합적으로 했기 때문에 가짜 Store를 만드는 것 보다 원래 Store를 사용하는 것이 맞다고 판단내렸다. redux-mock-store 라이브러리를 사용하지 않고 직접 Store를 생성하고 생성된 store에 dispatch를 통해 me의 데이터를 넣었다.

    await store.dispatch({
      type: LOAD_MY_INFO_SUCCESS,
      data: {
        Images: [],
        nickname: '우기',
      }
    });

이제 me 정보가 없어서 뜨는 오류가 해결되었다.



.find() 왜 변수저장하면 안되는거에요?


가장 오랜 시간이 필요한 부분이었다.😭

mount함수의 결과물을 component라는 변수에 넣어뒀고, 이 변수를 가지고 find선택자를 이용해서 textarea를 찾았다. 그리고 너무도 당연하게 변수를 설정해서 넣어뒀다. 이어서 expect함수로 테스트하려는데 계속 method “simulate” is meant to be run on 1 node. 0 found instead. 라는 에러가 뜬다.

당연히 find가 정상적으로 작동되지 않는다고 생각했고 검색했지만 별반 다른내용이 없었다. 그렇게 한참을 확인했다. 열심히 콘솔을 찍고 debug함수를 이용한 결과, 변수에 넣어둔 나의 코드가 문제였다.

결국 매번 component.find('textarea')와 같이 호출하니 정상적으로 실행되었다. 아직 이유를 찾지 못했다



useEffect가 적용이 안돼요!


나의 시나리오는 Store의 값이 바뀌고 이 값을 추적하는 useEffect가 바뀐 것을 확인하고 처리해야 했다. 그러나 dispatch 이후에도 처리되지 않았고, 확인한 결과 setProps()가 필요했다.

setProps함수는 root컴포넌트의 props를 설정하고 리렌더하는 함수이다.




나의 테스트


시나리오

  1. 게시글을 올리는 컴포넌트인 PostForm이 있다.
  2. PostForm은 store에서 me라는 객체가 필수적이다.
  3. form의 textarea에 글을 작성할 수 있다.
  4. form의 버튼(submit)을 통해 게시글을 등록할 수 있다.
  5. 등록이 완료되면 store의 addPostDone이라는 값이 true가 된다.
  6. useEffectaddPostDone을 확인하고 textarea의 내용을 지운다.

테스트

먼저 mount함수를 통해 test wrapper로 감싼 변수를 저장한다.

const component = mount(
      <Provider store={store}>
        <PostForm />
      </Provider>
    );

이후 PostForm의 textarea의 값을 변경한다.

component.find('textarea').simulate('change', {
      target: { value: 'testing' }
    });

변경이 정상적으로 되었는지 확인한다.

expect(component.find('textarea').props().value).toEqual('testing');

이후 게시물 등록을 처리한다.(button의 type이 submit이었습니다.)

 component.simulate('submit');

등록 처리를 위해 store에 dispatch를 한다.

store.dispatch({
      type: ADD_POST_SUCCESS,
      data: {
        User: {
          id: 1,
          nickname: '우기'
        },
        content: "안녕"
      }
    });

useEffect를 사용하기위해 update를 해준다.

component.setProps();

마지막으로 textarea에 값이 지워졌는지 확인한다.

expect(component.find('textarea').props().value.length).toBe(0);



마무리

이제서야 서버를 재시작하고 나의 브라우저에서 한 테스트보다 Jest와 Enzyme을 활용한 테스트가 조금은 더 중요하다고 느껴진다. 한 코드가 다른 코드를 더 나아가 다른 컴포넌트를 망가뜨리지 않으려면 이러한 유닛 테스트가 수반되어야 한다.

처음에는 복잡한 설정과 미칠듯한 에러가 나를 힘들게 했지만, 단 몇 초만에 결과가 나오는 이 모습은 개발당시 브라우저에서 일일이 확인하던 나의 모습을 다시금 보게 한다.

profile
하고 싶어 개발하는, 능동개발자

0개의 댓글