나의 React App을 테스트 한 것이 아니라, jest와 enzyme을 테스트 한 게 아닐까?
저의 App은 Next js을 기반으로 redux로 상태관리를 하며 redux-saga를 통해 side-effects를 처리했습니다. 또한 styled-component와 SCSS를 사용하여 style을 처리했습니다.
콘솔창의 pass를 본 나는 호기롭게 다음 작업을 구상했다.
expect
와 simulate
를 통한 테스트. 콘솔창에 뜬 무서운 빨간 글씨..
시작이다.
먼저 기본이 부족했다. 그저 pass 콘솔창을 보기위해 따라쳤기에 이런 기본적인 차이점도 확인하지 않고 성급히 테스트했다.
검색을 해보니 차이점은 shallow 함수는 컴포넌트를 자식 컴포넌트와 분리하여 테스트를 하고, mount 함수는 깊은 자식 컴포넌트 까지 테스트를 한다는 것이다.
redux
테스트를 하는 프로젝트는 <Provider>
로 감싸져 테스트가 진행되어야 한다. 따라서 mount함수로 진행해야 한다.
후에 .html()
과 .debug()
함수를 알게되어 확인해보니 shallow함수는 정말 자식 컴포넌트를 단순히 <Component />
로 처리하고 있었다.
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 하면 된다.
나의 테스트 컴포넌트는 useSelector
로 me
라는 객체를 가져와서 데이터를 출력한다. 그런데 테스트를 rudecer
, action
을 나눠서 하지 않고 통합적으로 했기 때문에 가짜 Store를 만드는 것 보다 원래 Store를 사용하는 것이 맞다고 판단내렸다. redux-mock-store
라이브러리를 사용하지 않고 직접 Store를 생성하고 생성된 store에 dispatch를 통해 me의 데이터를 넣었다.
await store.dispatch({ type: LOAD_MY_INFO_SUCCESS, data: { Images: [], nickname: '우기', } });
이제 me
정보가 없어서 뜨는 오류가 해결되었다.
가장 오랜 시간이 필요한 부분이었다.😭
mount
함수의 결과물을 component
라는 변수에 넣어뒀고, 이 변수를 가지고 find
선택자를 이용해서 textarea
를 찾았다. 그리고 너무도 당연하게 변수를 설정해서 넣어뒀다. 이어서 expect
함수로 테스트하려는데 계속 method “simulate” is meant to be run on 1 node. 0 found instead.
라는 에러가 뜬다.
당연히 find
가 정상적으로 작동되지 않는다고 생각했고 검색했지만 별반 다른내용이 없었다. 그렇게 한참을 확인했다. 열심히 콘솔을 찍고 debug
함수를 이용한 결과, 변수에 넣어둔 나의 코드가 문제였다.
결국 매번 component.find('textarea')
와 같이 호출하니 정상적으로 실행되었다. 아직 이유를 찾지 못했다
나의 시나리오는 Store의 값이 바뀌고 이 값을 추적하는 useEffect가 바뀐 것을 확인하고 처리해야 했다. 그러나 dispatch 이후에도 처리되지 않았고, 확인한 결과 setProps()가 필요했다.
setProps
함수는 root컴포넌트의 props를 설정하고 리렌더하는 함수이다.
submit
)을 통해 게시글을 등록할 수 있다.addPostDone
이라는 값이 true
가 된다.useEffect
가 addPostDone
을 확인하고 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을 활용한 테스트가 조금은 더 중요하다고 느껴진다. 한 코드가 다른 코드를 더 나아가 다른 컴포넌트를 망가뜨리지 않으려면 이러한 유닛 테스트가 수반되어야 한다.
처음에는 복잡한 설정과 미칠듯한 에러가 나를 힘들게 했지만, 단 몇 초만에 결과가 나오는 이 모습은 개발당시 브라우저에서 일일이 확인하던 나의 모습을 다시금 보게 한다.