
Jest is a delightful JavaScript Testing Framework with a focus on simplicity.
It works with projects using: Babel, TypeScript, Node, React, Angular, Vue and more!
Jest 공식 홈페이지는 Jest에 대해서 이렇게 설명하고 있습니다. Jest는 단순성에 중점을 둔 즐겁게 사용할 수 있는 JavaScript 테스팅 프레임워크입니다. 또한 Babel, TypeScript, Node, React, Angular, Vue에서 사용할 수 있다고 하죠.
단순성에 초점을 둔 즐겁게 사용할 수 있는 JavaScript 프레임워크 라는 말이 너무 멋진 말 같네요! 실제로도 코딩할때 테스트코드를 작성하면서 작업하는 비즈니스 로직은 견고하다는 느낌을 많이 받곤 합니다.
우리는 왜 테스트 코드를 작성해야하는 걸까요? 그 이유는 아래의 내용으로 설명할 수 있을 것입니다.
- 품질 보장: 테스트 코드는 소프트웨어의 품질을 보장합니다. 기능이 예상대로 작동하는지, 새로운 코드 변경으로 인해 기존의 기능에 문제가 발생하지 않는지 확인할 수 있습니다.
- 회귀 테스트: 기존 코드의 변경이나 리팩토링을 할 때, 테스트 코드를 통해 이전에 작성된 기능들이 여전히 올바르게 동작하는지 확인할 수 있습니다.
- 개발 속도 향상: 안정적인 테스트 코드가 있으면, 새로운 기능 개발이나 기존 기능의 변경을 보다 자신감 있게 수행할 수 있습니다.
- 문서화: 테스트 코드는 종종 실제 코드의 동작 방식에 대한 라이브 문서로 활용됩니다. 즉, 다른 개발자들이 해당 코드의 기능과 사용법을 이해하는 데 도움을 받을 수 있습니다.
- 디버깅: 문제가 발생했을 때, 테스트 코드를 통해 오류를 더 빠르게 찾아 해결할 수 있습니다.
테스트코드를 작성하면 적어도 위 5개의 내용에 대한 장점을 얻을 수 있으니, 테스트 코드를 작성해야겠죠? 하지만 테스트코드도 어떻게 작성하는지에 따라 많은 차이를 불러올 수 있습니다.
제가 다니고있는 회사에서도 테스트코드를 작성하고있는데, 거의 대부분의 테스트코드가 View 로직 대상으로 작성되고 있었습니다. 물론 나쁘다는 것은 아닙니다. Jest 라이브러리에서도 View 테스트하는 메소드도 제공해주고 있으니까요.
그렇지만 View 로직같은 경우에는 변경되는 케이스가 정말 많습니다. 지금 회사에서도
애자일
개발을 하고 있다 보니까 정말 자주 View가 바뀌곤 합니다. 하지만 테스트코드가 전부 View 로직과 관련이 있다 보니까 생각보다 변경하는 경우가 정말 빈번하게 일어나고 있는것을 알게 되었고 과연 애자일 개발을 하고있는 회사에서 View 로직 테스트만 진행하는것이 효율적인가? 에 대해서도 고민하게 되었습니다.왜 우리는 View 로직 테스트코드만 작성할까?
입사한지 2달 반 지난 지금 시점에 계속 신규기능 추가만 하다보니, 다른 프로젝트의 코드를 깊게 들여다볼 시간이 없었습니다. 최근들어 문제점을 인지하고 하나하나 확인하다보니 “아하! 이래서 View로직 테스트코드만 있었구나!” 라는 생각이 들었습니다.
우선 우리의 프로젝트에는 Business 로직과 View 로직을 합쳐서 하나의 Page를 구성하고 있었습니다. 프로젝트 아키텍처를 설계하고 고려한 작업한 것이 아닌것 같다는 느낌이 들었습니다. Business 로직을 분리하지 않으면서 중복된 Business 로직들이 전부 필요로하는 Page에 포함된 구조죠.
따라서 같은 Business로직이여도 Page마다 입맛대로 조금씩 변형된 구조로 존재했기에 테스트코드 작성에 어려움이 있었다고 생각합니다.
예) 하나의 상품이 있고, 그 상품들을 보관하는 카트가 있다고 가정해봅시다.
const Page = () => { const [cart, setCart] = useState<Cart[]>([]); const addCart = (product: Product) => { const findIndex = cart.findIndex((i) => i.productId === product.productId); if(findIndex !== -1) { setCart((i) => ([...i, {...product, quantity: 1}])); } else { const findItem = cart[findItem]; setCart((i) => [...i.slice(0, index), { ...findItem, quantity: findItem.quantity + 1 }, ...i.slice(index + 1)]); } } const removeCart = (product: Product) => { setCart((cart) => cart.filter(i => i.productId !== product.productId)); } return ( <div> <span>총 Cart ({cart.length})</span> // 상품 추가 { // 카트 리스트 & 카트 제거 } </div> ) }
위 내용에선 카트 추가, 제거, 카트 총 수량 이 3가지에 대해서만 작성했습니다. 하지만 이 페이지말고 다른 페이지에서 비즈니스 로직을 분리하지 않으면 똑같은 코드 내용을 해당 페이지에서도 작성해야 할 것입니다. 더 최악으로 간다면 해당 비즈니스 로직이 페이지에 종속된 내용으로 변경된다면 더 최악이겠죠..
그럼 우린 N개의 카트를 추가하는 페이지에서 똑같은 비즈니스 로직 테스트 코드를 작성해야할 수도 있습니다. 효율적으로 개발하기 위해서, 좀 더 견고한 코드를 작성하기 위해서 테스트코드를 작성하고 있는데 더 복잡해진 상황이죠..
지금 회사의 코드가 이런 느낌입니다. 리팩토링을 진행하기 위해서 하나하나 해보고 있는데, 실 서비스 중인 코드다 보니 도메인도 은근 많고 시간이 좀 많이 걸릴 것 같은 느낌이 들었습니다. 그렇다고 시간이 그렇게 많지도 않고요.. 어떻게 리팩토링을 진행해야 할지 막막하긴 합니다..
효율적으로 테스트코드를 작성하려면?
비즈니스 로직을 분리해야합니다. 중복된 비즈니스로직은 불필요한 테스트코드를 낳을뿐 아니라, 개발자의 성향에 따라 변경되는 코드 스타일로인해 파악하기도 어렵고, 유지보수하기 어려워질 것입니다. 규격화 된 하나의 비즈니스 로직을 가져다가 사용하는것이 좀 더 좋은 코드스타일 통일과, 불필요한 테스트코드 줄이기에 도움이 될 것입니다.
분리된 Business 로직에 해당하는 테스트코드만 작성한다면 해당 Business 로직을 불러다 사용하는 곳에선 효율적인 View 로직 테스트 코드만을 작성할 수 있을 것입니다. 그로인해 우리는 아래와 같은 효과를 얻을 수 있을 것입니다.
- Business 로직:
- 대부분의 프론트엔드 테스트는 비즈니스 로직 중심으로 작성됩니다. 이렇게 하면 핵심 로직이 올바르게 작동하는지 확인할 수 있습니다.
- 비즈니스 로직에 대한 단위 테스트(unit test)는 모듈, 함수 또는 컴포넌트의 독립적인 기능을 테스트합니다.
- View 로직:
- View 로직에 대한 테스트는 사용자 인터페이스와 상호 작용을 테스트하는 데 중점을 둡니다.
- 종종 통합 테스트(integration test) 또는 엔드 투 엔드 테스트(end-to-end test)의 일부로 수행됩니다. 이러한 테스트는 여러 컴포넌트나 시스템 전체가 함께 올바르게 작동하는지 확인합니다.
결론적으로, 프론트엔드에서는 Business 로직과 View 로직 모두에 대한 테스트 코드를 작성하는 것이 바람직합니다. Business 로직은 앱의 핵심 기능을 확인하고, View 로직은 사용자 경험과 인터페이스 상호 작용을 테스트합니다. 따라서 너무 Business로직, View로직만 테스트하기 보단 효율적인 코드로 둘 다 테스트하는 것이 완성도 있는 프로젝트를 만드는것에 많은 도움이 될 것입니다.
우리는 위 내용을 바탕으로 Nest.js 예제 코드 작성을 진행할 것입니다. Next.js에서 Jest 환경 설정부터, 여러 테스트 예제들까지 Jest 테스트 코드에 대해 완벽하게 적응할 수 있도록 함께하도록 합시다!