이번 글에서는 프로그래밍 테스트의 중요성과 실제 적용 방안에 대해서 다루도록 하겠습니다.

테스트는 작성된 코드가 의도대로 정확하게 작동하는지 확인하는 과정입니다.
단순히 말해서, 개발 과정에서 코드에 숨겨진 버그를 발견하고, 사용자에게 안정적인 서비스를 제공하는데 목적이 있습니다.
테스트에는 다양한 종류가 있는데, 보통 범위에 따라 단위 테스트, 통합 테스트드, E2E 테스트로 구분 할 수 있습니다.

리팩토링으로 유명한 마틴 파울러는 테스트의 중요성을 특히 강조했습니다. 그에 따르면 테스트를 먼저 작성하는 것은 여러모로 큰 이점을 가져다줍니다.
첫째, 테스트를 먼저 작성하면 코드의 API가 잘 설계되었는지 자연스럽게 검증할 수 있습니다.
테스트를 만들면서 원하는 기능을 추가하기 위해 무엇이 필요한지 깊이 고민하게 되므로, 이는 결국 좋은 설계를 이끌어내는 데 큰 도움이 됩니다.
둘째, 코드를 조금씩 변경하고 매번 테스트를 진행하면, 변경 폭이 작아 문제가 발생했을 때 훨씬 찾기 쉽습니다. 이처럼 대부분의 결함을 코드 제출 전에 미리 고칠 수 있기 때문에, 더 안정적인 배포가 가능해지는 것이죠.
셋째, 테스트 코드는 그 자체로 훌륭한 문서 역할을 합니다. 해당 기능을 구현하기 위한 테스트를 읽는 것만으로도 코드의 동작을 명확하게 이해할 수 있습니다.
마지막으로, 테스트는 코드 검증 시간을 줄여줍니다. 이미 잘 준비된 테스트 코드가 있다면, 코드 리뷰 단계에서 수동으로 코드를 검증하는 데 드는 시간을 크게 절약할 수 있습니다.
효율적이고 유지보수하기 좋은 코드를 작성하기 위해선 몇 가지 중요한 규칙이 있습니다.
인터페이스를 기준으로 테스트를 작성하기, 모든 테스트는 내부 구현이 아닌 외부에 노출되는 public 메서드를 기준으로 작성해야합니다.
캡슐화된 내부 구현에 대한 테스트는 불필요하게 많은 테스트 코드를 만들고, 구현이 조금만 변경되어도, 유지보수가 어렵기 때문입니다. 컴퍼넌트에 대한 테스트 코드는 사용자가 앱을 사용하는 방식으로 UI 구성 요소를 테스트 하는 것이 좋으며, 이는 테스트 신뢰성을 높입니다.
또한 커버리지보다는 의미 있는 테스트인지 고민하는 것이 좋습니다. 변경 가능성이 거의 없고 내부 로직이 너무 간단한 유틸리티 함수는 과감하게 테스트를 생략하는 것이 좋습니다. 이러한 함수들은 다른 모듈이나 컴퍼넌트의 로직이 포함 되었을때, 한번에 검증하는 것이 효율적이기 때문입니다.
그리고 잘 작성된 테스트 코드는 그 자체로 훌룡한 문서가 되지만, 명확한 테스크 설명이 있다면, 테스트 파일만 보고도 앱이 어떻게 동작하는지 쉽게 파악할 수 있기 때문에 가급적 가독성 있는 테스트 코드 작성하는 것이 좋습니다.
마지막으로 하나의 테스트에는 가급적 하나의 동작만이 검증하는 것이 좋습니다. 다양한 컴퍼넌트들이 조합된 시나리오를 검증해야한다면, 하나의 테스트에서 한번에 검증하기 보다는 여러 개의 개별 테스트로 나누어 검증하는 것이 가독성과 유지보수 측면에서 좋습니다.
테스트를 작성하는 일반적인 방법으로는 AAA(Arrange-Act-Assert) 패턴 또는 GWT(Given-When-Then) 패턴을 따릅니다.
먼저, 테스트를 위한 환경(Arrange/Given)을 만들고, 그 다음 테스트할 동작을 재현한 후(Act/When), 마지막으로 올바른 동작을 실행되었는지 또는 변경사항을 검증합니다.(Assert/Then)
it('className prop으로 설정한 css class가 적용된다.', async () => {
// Arrange - 테스트를 위한 환경 만들기
// -> className을 지닌 컴포넌트 렌더링
await render(<TextField className="my-class" />);
// Act - 테스트할 동작 발생 (렌더링 검증이므로 생략)
// Assert - 올바른 동작이 실행되었는지 검증
// -> 렌더링 후 DOM에 해당 class가 존재하는지 검증
expect(screen.getByPlaceholderText('텍스트를 입력해 주세요.')).toHaveClass('my-class');
});
예를 들어, 위에 코드는 TextFieldID 컴퍼넌트의 className prop에 설정된 CSS 클래스가 텍스트 필드에 제대로 적용되었는지 검증하는 코드입니다.
해당 코드에서는 컴퍼넌트를 랜더링하여, 환경을 설정하고(Arrange), 랜더링 검증이므로 특정 동작 재현(Act)은 생략하며, 마지마긍로 랜더링된 DOM에 해당하는 클래스가 존재하는지 검증(Assert)합니다.