diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3363fab..6b80d10 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,30 +1,22 @@
-# 워크플로우의 이름을 지정합니다
-# 대시보드에서 워크플로우를 구분할 때 사용됩니다
name: CI Pipeline
-# Event
-on: # 워크플로우가 실행될 조건을 정의합니다
- pull_request: # PR을 조건으로 설정합니다
+on:
+ pull_request:
branches:
- - test # test 브랜치로 PR이 열리면 실행되도록 지정
+ - test
-# Jobs: 필요한 Job을 병렬 또는 (의존성에 의해)순차적으로 실행
-jobs: # 아래 계층에 수행할 Job을 정의합니다
+jobs:
- # Build Job
build:
- runs-on: ubuntu-latest # 해당 Job의 러너 환경을 지정합니다
+ runs-on: ubuntu-latest
- # 아래에 build의 Step들을 정의합니다
steps:
- # 1. 리포지토리에서 소스 코드를 체크아웃(복제)
- - name: Checkout code # name으로 각 Step의 이름을 부여합니다
- uses: actions/checkout@v3 # uses를 통해 액션을 호출합니다
+ - name: Checkout code
+ uses: actions/checkout@v3
- # 2. Gradle 라이브러리 캐싱
- name: Cache Gradle packages
uses: actions/cache@v3
- with: # with를 통해 액션에 대한 추가적인 설정을 지정할 수 있습니다
+ with:
path: |
~/.gradle/caches
~/.gradle/wrapper
@@ -32,29 +24,47 @@ jobs: # 아래 계층에 수행할 Job을 정의합니다
restore-keys: |
${{ runner.os }}-gradle-
- # 3. Java 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- # 4. gradlew에 권한 부여
- name: Grant execute permission for gradlew
- run: chmod +x gradlew # run으로 직접 명령어를 실행할 수 있습니다
+ run: chmod +x gradlew
- # 5. 의존성 설치 및 빌드
- name: Build with Gradle
run: ./gradlew build --no-daemon
- # Test Job
- # 빌드에서 테스트를 진행하기 때문에 해당 Job은 제외해도 됩니다. 그러나 needs를 설명하기 위해 편의상 추가했습니다.
+ # 추가
+ - name: Leave a comment for test coverage
+ id: jacoco
+ uses: madrapps/jacoco-report@v1.2
+ with:
+ title: 📊 Test Coverage Report
+ paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml # JaCoCo 리포트 위치
+ token: ${{ github.token }}
+ min-coverage-overall: 80 # 전체 프로젝트의 커버리지가 80% 이상이어야 통과
+ min-coverage-changed-files: 80 # 수정된 파일에 대한 최소 커버리지가 80% 이상이어야 통과
+ pass-emoji: '✅' # 통과 이모지 (default : ":apple:")
+ # update-comment: true # true로 설정하는 경우 기존 코멘트를 업데이트하는 형태로 동작
+
+ # 슬랙 알림 추가
+ - name: Slack message with build result
+ if: always()
+ uses: 8398a7/action-slack@v3
+ with:
+ status: ${{ job.status }}
+ fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
+ if_mention: failure,cancelled
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
+
test:
runs-on: ubuntu-latest
- needs: build # build가 성공적으로 완료되어야 수행된다는 의미(의존성 설정)
+ needs: build
steps:
- # 기존 체크아웃, JDK 설치 과정
- name: Checkout code
uses: actions/checkout@v3
@@ -64,6 +74,5 @@ jobs: # 아래 계층에 수행할 Job을 정의합니다
distribution: 'temurin'
java-version: '17'
- # 6. 테스트 실행
- name: Run tests
run: ./gradlew test --no-daemon
\ No newline at end of file
diff --git a/README.md b/README.md
index 0c4bd86..20176ed 100644
--- a/README.md
+++ b/README.md
@@ -12,20 +12,24 @@
2. [🗣️ GitHub Actions 소개](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EF%B8%8F-github-actions-%EC%86%8C%EA%B0%9C)
3. [🎯 GitHub Actions 주요 개념](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#-github-actions-%EC%A3%BC%EC%9A%94-%EA%B0%9C%EB%85%90)
4. [🚀 GitHub Actions 사용법](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#-github-actions-%EC%82%AC%EC%9A%A9%EB%B2%95)
- * [대략적인 과정](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EB%8C%80%EB%9E%B5%EC%A0%81%EC%9D%B8-%EA%B3%BC%EC%A0%95)
- * [예시](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%98%88%EC%8B%9C)
- * [`uses` 필드](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#uses-%ED%95%84%EB%93%9C)
- * [`run` 필드](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#run-%ED%95%84%EB%93%9C)
+ * [대략적인 과정](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EB%8C%80%EB%9E%B5%EC%A0%81%EC%9D%B8-%EA%B3%BC%EC%A0%95)
+ * [예시](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%98%88%EC%8B%9C)
+ * [`uses` 필드](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#uses-%ED%95%84%EB%93%9C)
+ * [`run` 필드](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#run-%ED%95%84%EB%93%9C)
5. [🗃️ 캐싱(Caching)](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EF%B8%8F-%EC%BA%90%EC%8B%B1caching)
6. [❌ 테스트 실패 살펴보기](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%A4%ED%8C%A8-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0)
7. [☑️ 조건문 사용하기](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EF%B8%8F-%EC%A1%B0%EA%B1%B4%EB%AC%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0)
8. [💬 슬랙으로 알림 설정하기(Secrets 사용)](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#-%EC%8A%AC%EB%9E%99%EC%9C%BC%EB%A1%9C-%EC%95%8C%EB%A6%BC-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0secrets-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0)
- * [슬랙 채널 추가](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%8A%AC%EB%9E%99-%EC%B1%84%EB%84%90-%EC%B6%94%EA%B0%80)
- * [슬랙 앱 생성: Webhook URL 발급](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%8A%AC%EB%9E%99-%EC%95%B1-%EC%83%9D%EC%84%B1-webhook-url-%EB%B0%9C%EA%B8%89)
- * [시크릿 등록](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%8B%9C%ED%81%AC%EB%A6%BF-%EB%93%B1%EB%A1%9D)
- * [워크플로우 작성1: `slack-github-action`(버그)](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%91%EC%84%B11-slack-github-action%ED%98%84%EC%9E%AC-%EB%B2%84%EA%B7%B8-%EC%9E%88%EC%9D%8C)
- * [워크플로우 작성2: `slack-github-action`(버그 우회)](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%91%EC%84%B12-slack-github-action%EB%B2%84%EA%B7%B8-%EC%9A%B0%ED%9A%8C)
- * [워크플로우 작성3: `action-slack`](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%91%EC%84%B13-action-slack)
+ * [슬랙 채널 추가](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%8A%AC%EB%9E%99-%EC%B1%84%EB%84%90-%EC%B6%94%EA%B0%80)
+ * [슬랙 앱 생성: Webhook URL 발급](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%8A%AC%EB%9E%99-%EC%95%B1-%EC%83%9D%EC%84%B1-webhook-url-%EB%B0%9C%EA%B8%89)
+ * [시크릿 등록](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%8B%9C%ED%81%AC%EB%A6%BF-%EB%93%B1%EB%A1%9D)
+ * [워크플로우 작성1: `slack-github-action`(버그)](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%91%EC%84%B11-slack-github-action%ED%98%84%EC%9E%AC-%EB%B2%84%EA%B7%B8-%EC%9E%88%EC%9D%8C)
+ * [워크플로우 작성2: `slack-github-action`(버그 우회)](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%91%EC%84%B12-slack-github-action%EB%B2%84%EA%B7%B8-%EC%9A%B0%ED%9A%8C)
+ * [워크플로우 작성3: `action-slack`](https://github.com/seungki1011/CICD-using-Github-Actions/tree/main?tab=readme-ov-file#%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%9E%91%EC%84%B13-action-slack)
+9. [📊 JaCoCo를 이용한 코드 커버리지 추가]()
+ * [`build.gradle` 설정]()
+ * [워크플로우 추가]()
+
@@ -40,11 +44,10 @@
-아래 브랜치는 `main`을 기반으로 합니다.
-
* **[`slack/slack-github-action-bug`](https://github.com/seungki1011/CICD-using-Github-Actions/tree/slack/slack-github-action-bug)**: `slackapi/slack-github-action@v1.27.0`을 사용합니다. 버그가 있습니다.
* **[`slack/slack-github-action-workaround`](https://github.com/seungki1011/CICD-using-Github-Actions/tree/slack/slack-github-action-workaround)**: `slackapi/slack-github-action@v1.27.0`을 사용하지만, 버그를 우회한 방법입니다.
* **[`slack/action-slack`](https://github.com/seungki1011/CICD-using-Github-Actions/tree/slack/action-slack)**: `8398a7/action-slack`을 사용합니다
+* **[`coverage/jacoco-report`](https://github.com/Madrapps/jacoco-report?tab=readme-ov-file)**: `Madrapps/jacoco-report`를 사용합니다. (슬랙 알림도 추가)
@@ -169,7 +172,7 @@ Github Action으로 워크플로우를 정의해서 사용하기 위해서는
---
-### 예시 1
+### 예시
워크플로우를 정의해서 사용하기 위해서 `.github/workflows/` 아래에 `ci.yml`이라는 파일을 만들어서 사용합니다.
@@ -941,12 +944,199 @@ jobs:
+---
+
+## 📊 JaCoCo를 이용한 코드 커버리지 추가
+
+### `build.gradle` 설정
+
+[JaCoCo (Java Code Coverage)](https://docs.gradle.org/current/userguide/jacoco_plugin.html)는 **Java 애플리케이션의 코드 커버리지를 측정하기 위한 오픈 소스 도구**입니다. 코드 커버리지는 단위 테스트나 통합 테스트에서 코드의 어느 부분이 실행되었는지를 나타내는 지표입니다. 이를 통해 작성된 테스트가 얼마나 효과적인지, 코드의 어느 부분이 테스트되지 않았는지 확인할 수 있습니다.
+
+JaCoCo를 이용해서 **코드 커버리지(code coverage)를 측정**하고, **해당 커버리지 리포트를 댓글(comment)로 달아주는 워크플로우를 추가** 해봅시다.
+
+
+
+먼저 JaCoCo를 사용하기 위해서 `build.gradle`에 필요한 설정을 추가해줍니다.
+
+```groovy
+plugins {
+ // ...
+ id 'jacoco' // 추가
+}
+
+jacoco {
+ toolVersion = "0.8.10" // jacoco 버전 명시
+}
+
+jacocoTestReport {
+ reports {
+ xml.required = true // madrapps/jacoco-report를 사용하기 위해서 xml 리포트 사용
+ html.required = true
+ }
+
+ // 각 리포트 타입 마다 저장 경로를 설정할 수 있다
+ // html.destination file("$buildDir/jacoco/html")
+ // xml.destination file("$buildDir/jacoco/xml")
+
+ // xml 기본 저장 경로: $buildDir/reports/jacoco/test/jacocoTestReport.xml
+
+ // 보고서에 표시되는 걸 제외하고 싶은 클래스를 명시
+ afterEvaluate {
+ classDirectories.setFrom(
+ files(classDirectories.files.collect {
+ fileTree(dir: it, excludes: [
+ // 나의 프로젝트에 맞게 변경 {그룹이름}/{프로젝트명}
+ "seungki/cicdpractice/api/domain/**",
+ "**/*Application*",
+ "**/*Request*",
+ "**/*Response*",
+ "**/*Exception*"
+ ])
+ })
+ )
+ // 보고서를 생성하고 나서야 테스트 커버리지 검증을 진행
+ finalizedBy(jacocoTestCoverageVerification)
+}
+
+// 커버리지 검증을 위한 기준 제시
+jacocoTestCoverageVerification {
+ violationRules {
+ rule {
+
+ /**
+ * element: 커버리지를 체크하는 기준
+ *
+ * BUNDLE: 전체 프로젝트의 모든 파일(default)
+ * CLASS: 클래스
+ * METHOD: 메서드
+ * PACKAGE: 패키지
+ * SOURCEFILE: 소스 파일
+ **/
+ enabled = true
+ element = 'CLASS'
+
+ /**
+ * counter: 커버리지 측정을 위한 최소의 단위
+ *
+ * BRANCH: 조건문의 분기 수
+ * CLASS: 클래스의 수
+ * COMPLEXITY: 복잡도
+ * INSTRUCTION: Java 바이트코드 명령의 수(default)
+ * METHOD: 메서드의 수
+ * LINE: 빈 줄을 제외한 실제 코드의 라인 수
+ **/
+
+ /**
+ * value: 커버리지의 측정 메트릭(metric)
+ *
+ * TOTALCOUNT: 전체 개수
+ * MISSEDCOUNT: 커버되지 않은 개수
+ * COVEREDCOUNT: 커버된 개수
+ * MISSEDRATIO: 커버되지 않은 비율. 0 ~ 1 사이의 숫자로, 1이 100%.
+ * COVEREDRATIO: 커버된 비율. 0 ~ 1 사이의 숫자로, 1이 100%. (default)
+ **/
+
+ limit {
+ counter = 'LINE'
+ value = 'COVEREDRATIO'
+ minimum = 0.80 // value에 대한 최소 통과 기준
+ }
+
+ // 커버리지 체크를 제외할 클래스를 명시
+ // jacocoTestReport과 가르게 패키지 경로를 적어줘야 한다
+ excludes = [
+ "seungki.cicdpractice.api.domain.**",
+ "**.*Application*",
+ "**.*Request*",
+ "**.*Response*",
+ "**.*Exception*"
+ ]
+ }
+ }
+}
+
+tasks.named('test') {
+ useJUnitPlatform()
+ finalizedBy jacocoTestReport // 항상 테스트가 완료되어야 리포트 생성
+}
+```
+
+
+
+`.gradlew test` 또는 인텔리제이의 Gradle 도구에 들어가서 `Tasks>verification>test`를 실행해봅시다.
+
+
+
+![cifail2](./img/README/gradletest.png)
+
+
Gradle > Tasks > verification > test
+ +service 커버리지의 최소 조건을 만족하지 못한다
+ +현재 컨트롤러 계층의 테스트만 작성했기 때문에, **서비스 계층에 대한 커버리지 조건을 만족하지 못하여 실패**했습니다. 서비스에 대한 **테스트를 추가하고 다시 시도**해보겠습니다. + +최소 기준을 만족하고, 통과된다
+ +