분류 Reactjs

React 버전 16의 단위 테스트 모범 사례

컨텐츠 정보

  • 조회 313 (작성일 )

본문

2017 년 React 버전 16이 도입되면서 이제 후크를 통해 구성 요소간에 상태 저장 논리를 재사용 할 수 있습니다. 

그러나 이러한 새로운 기능을 통해 엔지니어는 React 사용을 위해 이전에 정의 된 규칙을 다시 방문해야 합니다. 

단위 테스트는 이러한 관행 중 하나입니다.


https://eng.uber.com/best-practices-for-react-v16/ 


Web Tools Platform 팀의 임무는 Uber의 Advanced Technologies Group (ATG) 팀이 자율 주행 차량을 위한 더 나은 도구를 개발하고 부분적으로 오픈 소스 소프트웨어를 제작할 수 있도록 하는 성능이 뛰어난 웹 시각화를 만드는 것입니다.


Uber ATG의 Web Tools Platform 팀에서 여름 엔지니어링 인턴으로, 우리 팀이 자율 주행 차량 데이터를 시각화 하기 위해 애플리케이션의 일부 로직을 크게 변경 한 후 다양한 React 컴포넌트를 테스트하기 위한 새로운 모범 사례를 탐색하고 정의했습니다.

우리 팀은 후크로 기능적 구성 요소를 구현하는 동안 온라인에서 사용 가능한 React 버전 16 단위 테스트에 대한 문서가 부족함을 발견했습니다. 이 과정에서 후크를 사용하여 기능 구성 요소를 단위 테스트하기 위한 다양한 모범 사례를 결정했습니다. 다른 엔지니어링 팀이 이러한 모범 사례를 유용하게 활용하고 이러한 방법을 단위 테스트에 적용 할 수 있기를 바랍니다.


구성 요소 테스트를 위한 기본 설정 


인턴으로 시작했을 때 Web Tools Platform 팀은 자율 주행 차량 데이터를 시각화 하기 위해 응용 프로그램의 다양한 구성 요소를 추가하고 리팩토링을 마쳤습니다. 특정 구성 요소가 여전히 예상대로 작동하는지 확인하기 위해 이러한 변경 사항과 새로운 논리를 테스트 해야 했습니다. 이 평가 과정에서 React 버전 16 구성 요소를 테스트 하기 위한 모범 사례와 절차를 정리했습니다.


시작하기 위해 Enzyme을 사용하여 구성 요소를 얕게 렌더링 한 다음 스냅 샷과 비교할 수 있었습니다.


image2-e1566340187741.png 


이러한 렌더링 생성은 두 단계 프로세스입니다.

  1. 컴포넌트의 얕은 렌더링 주위에 랩퍼를 정의하십시오. 필요한 경우 소품을 포함하십시오.
  2. 리턴 된 JSX가 정확하고 예상 한 것과 일치하는지 확인하기 위해 기본 스냅 샷 테스트를 작성하십시오. 스냅 샷 테스트를 수행하려면 구성 요소를 렌더링하고 JSX 출력의 스냅 샷을 작성한 다음 테스트에 저장된 참조 스냅 샷 파일과 비교하십시오. 두 스냅 샷이 일치하지 않으면 스냅 샷 테스트가 실패합니다.

스냅 샷은 구성 요소가 렌더링 하는 것에 대한 개요를 제공하지만 여전히 구성 요소의 내부 논리를 테스트 해야 합니다.


클래스 컴포넌트의 로직 테스트 


React 버전 16에서 클래스 컴포넌트의 로직 테스트는 매우 간단합니다. 개발자는 클래스 구성 요소 로직을 테스트하여 특정 클래스 컨텍스트의 변수를 참조하기 위해 상태 변수 및 소품에 액세스 할 수 있습니다.


따라서 클래스 구성 요소의 논리를 테스트하는 첫 번째 단계는 래퍼의 기본 클래스 인스턴스를 검색하는 것입니다.


image3-1-e1566340648116.png 


내부 함수 테스트 


일반적으로 클래스 컴포넌트에서 내부 함수의 논리를 테스트 한 후 React가 변경된 값을 호출 한 후 예상 한대로 변경했는지 확인했습니다. 우리 팀은 특정 조건이 유지되면 상태 변수를 변경하는 특정 기능을 테스트했습니다. 함수가 호출 된 후 변수 값이 변경되었는지 여부를 평가했습니다.


image4-1-e1566340818891.png 


componentDidMount가 올바르게 실행되었는지 테스트 


클래스 구성 요소에서 render 이외의 다른 함수는 선택 사항입니다. 그러나 위의 예에서 구성 요소에는 componentDidMount 메서드가 포함되어 있는데,이 메서드는 구성 요소가 마운트 해제 될 때까지 상태 필드의 값을 주기적으로 업데이트하는 간격을 설정합니다. 이 기능을 테스트하기 위해 componentDidMount가 상태 변수를 함수로 올바르게 설정했는지 확인했습니다.


함수가 반환 한 값이 올바른지 또는 올바른지 직접 테스트하지 않았습니다. 이 테스트는 즉각적 기능인 componentDidMount의 논리가 올바른지 확인합니다. 함수의 논리를 평가하려면 별도의 테스트를 수행해야 합니다.


image5-e1566341033563.png 


componentWillUnmount가 올바르게 실행되는지 테스트 


정의 된 경우 componentWillUnmount는 구성 요소가 원래 마운트 된 DOM (Document Object Model) 요소에서 마운트 해제되기 직전에 호출됩니다. componentWillUnmount는 구성 요소의 간격을 지워서 정리의 일부로 각 간격 후에 상태 변수의 setState가 업데이트 되지 않도록 합니다. 테스트에서 componentWillUnmount가 호출 된 후 래퍼에 정의되지 않은 intervalId가 있는지 확인하여 삭제되었음을 보여줍니다. 랩퍼의 intervalId가 있으면 componentWillUnmount가 제대로 작동하지 않을 수 있습니다.


함수형 컴포넌트 


클래스 구성 요소와 달리 인스턴스를 사용하여 기능 구성 요소를 테스트 할 수 없습니다. 함수 컴포넌트는 본질적으로 함수일 뿐이므로 인스턴스화 한 다음 직접 변수 나 함수를 호출 할 수 있는 방법이 없습니다.


이 문제를 극복하기 위해 가장 좋은 방법은 함수 구성 요소 내에서 더 복잡한 논리 세그먼트를 분리하여 직접 구성 요소 외부의 다른 파일에서 고유 한 방법으로 이름을 바꾸는 것입니다.


image8-e1566341260886.png 


내부 함수  테스트 


위의 예에서는 함수 구성 요소 버전 onOpen을 활용합니다.


이를 해결하기 위해 대량의 로직을 onOpen에서 Utils 파일로 옮겼으며 새로 작성된 함수를 내보내고 상태 변수 selectOpen 및 해당 set 메소드를 호출하여 호출합니다. 이러한 방식으로 onOpen의 논리를 테스트 할 수 있습니다.


onOpen과 같은 함수형 구성 요소를 테스트 할 때 localSetSelectOpen이 호출되었는지 확인해야 합니다. 그러나 클래스 구성 요소를 평가할 때 수정 한 직후 상태 변수의 값을 확인할 수 있습니다.


image7-e1566341479726.png 


함수 구성 요소의 내부 기능을 테스트하는 또 다른 예는 onSelectChange입니다. 원래 함수는 매개 변수를 가져 와서 조건부 if 문 세트에서 수행 할 조치를 결정했습니다. 이제 onSelectChange는 매개 변수를 가져와 위에서 설명한 Utils에서 내 보낸 함수 인 onSelectChangeLogic을 호출합니다. 이를 통해 onSelectChange의 원래 로직을 별도의 액세스 가능한 함수로 테스트 할 수 있습니다.


image6-e1566341641405.png 


두 테스트 모두 모의 함수를 포함하는 매개 변수 집합을 사용하여 onSelectChangeLogic을 호출합니다. 함수가 예상대로 작동하는지 확인하기 위해 실행 중에 특정 모의 함수가 호출되었는지 확인합니다.


mocking 후크 및 함수 사용 


테스트에 얕은 렌더링을 통합하고 추가 하위 구성 요소가 아닌 특정 구성 요소 만 테스트 하려고 했기 때문에 Jest의 모의 함수를 사용하여 테스트를 작성했습니다.


image11-e1566341815272.png 


이 테스트에서는 자식 구성 요소를 직접 호출 할 수 있도록 myFunctionandmockHook을 속여야 합니다. 위의 이전 테스트에서 조건 1과 2가 유지되지 않는지 확인하기 위해 모의 매개 변수 함수 myFunction을 전달합니다. 테스트는 myFunction이 호출되었는지 확인하기 때문에 실제로 구현할 필요는 없지만 myFunction의 동작을 속여야 합니다.


위의 이전 테스트에서 조건부 2가 유지되는지 확인하면 후크가 호출됩니다. 따라서 React가 매개 변수 예제처럼 전달하지 않으므로 후크를 별도로 속여야 합니다. 다행히 Jest는 후크와 반환 값을 속이는 방법을 제공합니다. 이런 식으로, 우리는 로컬로 가지고 있는 모의 함수인 mockHook으로서 후크를 속일 수 있습니다.


지역 변수의 참조를 유지하려면 모의 후크를 할당해야 한다는 것을 알았습니다. jest.fn()은 사용되지 않은 새로운 모의 함수를 반환하기 때문입니다. 후크 함수가 jest.fn()에 직접 할당되면 테스트 할 때 호출 당 참조를 추적 할 수 없습니다. 반대로 모의 후크를 연결하면 로컬 변수를 쉽게 모니터링 할 수 있습니다.


myHook을 로컬 변수로 설정하면 변수 이름 앞에 mock (대소 문자 구분)이 필요하다는 것을 알게 되었습니다. 외부 메소드를 속일 때 jest.mock()이 범위 밖의 변수를 참조 할 수 없기 때문입니다. 그러나 관련 함수 이름의 시작 부분에 특수 키워드 "mock"을 사용하는 한 로컬 변수를 사용하여 특정 모의 함수를 추적 할 수 있다는 것을 깨달았습니다. 변수에 "mock"을 접두사로 사용하면 속인 항목이 액세스 할 수 있는 대상이 명확하게 유지됩니다.


모의 useEffect 


React 내부 후크 인 useEffect (이전 예제와 달리)는 렌더링 직후와 부작용을 수행하기 위해 모든 업데이트 후에 호출됩니다. 또한 맞춤형 후크와 유사한 방식으로 테스트 할 수도 있습니다.


image10-e1566342637144.png 


위의 클래스 구성 요소에 있는 componentDidMount 및 componentWillUnmount 예제의 함수 구성 요소 버전입니다. 이 버전은 useEffect를 제외하고 해당 인스턴스와 동일한 테스트 방법을 사용합니다. useEffect에서 발생하는 논리를 테스트하기 위해 Utils에서 내 보낸 경우 onEveryInterval을 개별적으로 테스트합니다.


Key takeaways 


단위 테스트는 대규모 응용 프로그램 또는 프로젝트를 구축 할 때 엔지니어가 프로젝트의 특정 구조를 추가하거나 조작하여 다른 구조가 중단되었는지 여부를 테스트 할 수 있도록 하는 필수 도구입니다. React 버전 16으로 단위 테스트를 하기 위해 Uber ATG의 팀은 Enzyme을 사용하여 얕은 렌더링 주위에 래퍼를 만들고 Jest는 변수와 후크를 모의했습니다.


경험상 React 버전 16으로 클래스 구성 요소를 테스트하는 것은 비교적 간단합니다. 랩퍼의 기본 클래스 구성 요소 인스턴스를 검색하여 내부 기능이 예상대로 작동하도록 합니다.


반면에 함수형 구성 요소는 본질적으로 기능적이므로 인스턴스화 할 수 없습니다. 이러한 구성 요소를 테스트하려면 내부 논리를 분리하고 별도의 방법을 만들어야 합니다. 그런 다음 Jest를 사용하여 이러한 방법을 테스트합니다.


다른 React 개발자들도 이러한 모범 사례가 도움이 되기를 바랍니다.