배경
기존 코드를 수정하다 보면 예상치 못한 부분에서 문제가 발생하는 경우가 종종 있습니다. 프로덕션에 코드가 반영된 후에 테스트 실패를 발견하면 수정이 지연되어 서비스 안정성에 영향을 미칠 수 있습니다.
이런 문제를 방지하기 위해 테스트 자동화와 함께 PR에서 테스트 결과를 바로 보여주고 실패 지점을 명확하게 알려주는 CI 환경이 필요했습니다. 물론 개발자가 매번 신중하게 코드를 작성하고 테스트를 실행하면 되겠지만, 인간은 누구나 실수를 하기 마련입니다😅 그렇다면 이런 실수를 자동으로 잡아주는 CI가 있다면 얼마나 든든할까요?
GitHub Actions를 활용해서 PR이 올라올 때마다 자동으로 테스트를 실행하고, 테스트 결과를 PR 코멘트로 표시하며, 실패한 테스트의 위치와 이유를 알려주는 CI를 구축해 보겠습니다.
PR 코멘트로 테스트 결과 표시하기
먼저 테스트 결과를 PR에 시각적으로 표시해 볼까요? publish-unit-test-result-action을 사용하면 테스트 결과를 깔끔한 표 형태로 정리할 수 있습니다. 이 액션은 JUnit XML 형식의 테스트 결과 파일을 읽어서 '전체 테스트 수행 결과 요약, 성공/실패한 테스트 개수, 테스트 실행 시간, 실패한 테스트의 상세 정보'를 제공합니다. 이렇게 하면 리뷰어는 물론 개발자 본인도 PR을 확인할 때 테스트 결과를 한눈에 파악할 수 있어 유용합니다.
실패한 테스트 코드에 annotation 추가하기
어떤 테스트가 실패했는지, 실패한 테스트의 정확한 위치가 어디인지 알 수 있다면 훨씬 빠르게 문제를 해결할 수 있겠죠?
Vitest는 GitHub Actions Reporter를 제공합니다.
이 리포터를 사용하면 실패한 테스트를 GitHub PR에서 바로 확인할 수 있습니다.
리포터를 활성화하려면 vitest.config.ts
또는 vite.config.ts
파일에 아래와 같이 설정을 추가해 주세요.
export default defineConfig({
// 생략
test: {
reporters: process.env.GITHUB_ACTIONS
? ['junit', 'default', 'github']
: ['junit', 'default'],
outputFile: 'test-results.xml',
}
})
각 리포터는 서로 다른 형태로 테스트 결과를 출력합니다.
junit
리포터를 통해 생성된 XML 형태의 테스트 결과는 publish-unit-test-result-action에서 활용됩니다.
outputFile
은 junit
리포터가 테스트 결과를 저장할 파일명을 지정하기 위해 필요합니다.
test-results.xml
은 버전 관리할 필요가 없기 때문에 .gitignore
에 추가했습니다.
default
리포터는 일반적인 콘솔 출력을 제공합니다.
github
리포터는 GitHub Actions 전용 annotation을 생성합니다.
이제 test-results.xml
파일을 GitHub Actions에서 어떻게 활용하는지 workflow 설정을 통해 알아보겠습니다.
workflow 설정
.github/workflows/vitest.yml
파일을 생성하고 아래와 같이 작성해 주세요.
# workflow 이름
name: vitest
# 언제 이 workflow를 실행할지 정의
on:
pull_request:
# develop, feature로 시작하는 브랜치를 대상으로 함
branches: [develop, 'feature/**'],
# PR이 열리거나 새 커밋이 푸시될 때만 실행
types: [opened, synchronize]
jobs:
test:
runs-on: ubuntu-latest
# 이 job이 GitHub API에 접근할 수 있는 권한 설정
# https://github.com/EnricoMi/publish-unit-test-result-action?tab=readme-ov-file#permissions 참고
permissions:
contents: read
issues: read
checks: write
pull-requests: write
steps:
# 1. 저장소 코드를 runner에 다운로드
- name: Checkout repository
uses: actions/checkout@v4
# 2. Node.js 환경 설정
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
# 3. Yarn 패키지 매니저 전역 설치
- name: Install Yarn
run: npm install -g yarn
# 4. Yarn 캐시 복원으로 설치 시간 단축
- name: Restore Yarn cache
uses: actions/cache@v4
with:
path: .yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
# 5. 프로젝트 의존성 설치
- name: Install dependencies
run: yarn install
# 6. Vitest 실행
- name: Run Vitest
run: yarn vitest
# 7. 테스트 결과를 PR에 코멘트로 남기기
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
# 테스트가 실패해도 결과를 남기기 위해 이 단계는 항상 실행
if: always()
with:
files: |
test-results.xml
check_name: Unit Test Report
앞서 설명한 기능들을 모두 연결하는 workflow입니다.
PR이 올라올 때마다 자동으로 테스트를 실행한 후,
Vitest의 junit
리포터가 생성한 test-results.xml
파일을 publish-unit-test-result-action
이 읽어 테스트 결과 코멘트를 남기고,
github
리포터가 생성한 annotation을 통해 실패한 테스트의 정확한 위치와 원인을 코드에 직접 표시합니다.
실제 동작 결과
PR을 올리면 아래와 같이 코멘트로 테스트 결과를 확인할 수 있습니다.

테스트 코드에 수정된 내용이 있을 경우 새로운 코멘트가 추가되는 것이 아니라, 기존 코멘트가 업데이트됩니다.

테스트가 실패하는 경우에는 아래와 같이 annotation이 추가됩니다. Expected/Received 값을 바로 비교할 수 있습니다.

마치며
이번 글에서는 GitHub Actions를 활용해 PR에 테스트 결과를 코멘트로 남기고, 실패한 테스트 코드에 annotation까지 자동으로 표시하는 CI 구축 방법을 소개했습니다.
이러한 자동화의 효과는 생각보다 큽니다. 리뷰어는 PR을 열자마자 테스트 상태를 한눈에 확인할 수 있고, 개발자는 실패한 테스트를 찾아 헤매는 시간 없이 바로 문제 지점으로 이동할 수 있습니다. 결과적으로 코드 리뷰 시간은 단축되고, 버그 수정 속도는 빨라지며, 프로덕션 배포에 대한 신뢰도가 높아집니다.
한 번 구축해 놓으면 팀 전체가 실수를 줄이고 안심할 수 있는 개발 문화를 만들어 나갈 수 있기에 의미 있는 작업이었습니다. 이런 자동화들이 쌓여서 팀에 건강한 테스트 문화가 자리 잡는 데 도움이 되면 좋겠습니다.