1. 개요
코드 품질을 보장하기 위해单元测试(단위 테스트)는 필수적인 요소입니다. 그렇다면 테스트의 품질을 어떻게 측정할 수 있을까요? 바로 커버리지(Coverage) 지표를 사용하면 됩니다. 본 문서에서는 Java 프로젝트에서 테스트 커버리지를 측정하는 도구인 JaCoCo를 Maven 프로젝트에 통합하고, SonarQube에서 결과를 확인하는 방법을 설명합니다.
JaCoCo는 "Java Code Coverage"의 약자로, Java 애플리케이션의 코드 커버리지를 분석하는 데 특화된 도구입니다. SonarQube 설치 방법은 별도의 문서를 참고하시기 바랍니다.
2. 커버리지 기본 개념
테스트 커버리지는 테스트 코드가 소스 코드를 얼마나 충분히 검증했는지를 나타내는 지표입니다. 대표적인 측정 방식은 다음과 같습니다:
- 라인 커버리지(Line Coverage): 실행된 코드 라인수 / 전체 코드 라인수로, 테스트가 몇 개의 라인을 실행했는지 나타냄
- 클래스 커버리지(Class Coverage): 테스트된 클래스 수 / 전체 클래스 수
- 브랜치 커버리지(Branch Coverage): 실행된 분기 수 / 전체 분기 수로, if/else 분기가 모두 테스트되었는지 확인
- 메서드 커버리지(Method Coverage): 실행된 메서드 수 / 전체 메서드 수로, 모든 메서드가 테스트되었는지 검증
- 순환 복잡도(Cyclomatic Complexity): 코드 구조의 복잡도를 측정하며, JaCoCo는 예외 처리 분기를 제외하고 계산함. 일반적으로 순환 복잡도가 10을 초과하면 위험하다고 판단하며, 15 이하여야 하는 엄격한 규칙을 적용하는 경우도 있음
색상 표시
JaCoCo는 코드 커버리지 상태를 색상으로 구분하여 표시합니다:
- 빨간색: 커버리지 없음
- 초록색: 완전 커버리지
- 노란색: 부분 커버리지
실행 방식
JaCoCo는 다양한 실행 환경을 지원합니다:
- 명령행 직접 실행
- Ant 빌드 도구 연동
- Maven 플러그인 실행
- IDE 통합 도구 사용
본 문서에서는 Maven 기반 실행 방법을 중심으로 설명합니다.
3. Maven 통합
3.1 기본 설정
Maven 프로젝트에 JaCoCo를 통합하려면 pom.xml에 다음 플러그인 구성을 추가합니다:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<skip>${maven.test.skip}</skip>
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
<output>file</output>
<append>true</append>
<excludes>
<exclude>com/example/util/PerformanceTest/**</exclude>
<exclude>com/example/config/LoadConfig</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<phase>test-compile</phase>
</execution>
<execution>
<id>jacoco-site</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
mvn clean test 명령을 실행하면 커버리지 데이터 파일이 생성됩니다. 이 파일은 사람이 직접 읽을 수 없으나, SonarQube와 IntelliJ IDEA에서 해석할 수 있습니다. IntelliJ IDEA에서는 Run -> Show Code Coverage Data 메뉴를 통해 확인할 수 있습니다.
mvn clean verify 명령을 실행하면 target/site/jacoco/ 디렉토리에 다양한 형식의 리포트가 생성됩니다. index.html 파일을 브라우저로 열어 확인하면 됩니다.
3.2 검사 제외 설정
특정 클래스나 패키지를 커버리지 검사에서 제외할 수 있습니다:
<excludes>
<exclude>com/example/util/PerformanceTest/**</exclude>
<exclude>com/example/config/LoadConfig</exclude>
</excludes>
3.3 규칙과 임계값 설정
rules 태그를 사용하여 커버리지 임계값을 설정할 수 있습니다. 예를 들어, 클래스 커버리지를 100%로 요구하거나 메서드 커버리지 비율을 설정할 수 있습니다:
<rules>
<rule implementation="org.jacoco.maven.RuleConfiguration">
<element>BUNDLE</element>
<limits> <limit implementation="org.jacoco.report.check.Limit">
<counter>METHOD</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
<limit implementation="org.jacoco.report.check.Limit">
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
<limit implementation="org.jacoco.report.check.Limit">
<counter>CLASS</counter>
<value>MISSEDCOUNT</value>
<maximum>0</maximum>
</limit>
</limits>
</rule>
</rules>
위 규칙을 적용하려면 다음 execution을 추가해야 합니다:
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
</execution>
임계값을 충족하지 못하면 Maven 빌드가 실패합니다. SonarQube를 사용하는 경우, SonarQube 플랫폼에서 규칙과 임계값을 직접 설정할 수도 있습니다.
4. SonarQube 연동
SonarQube에 커버리지 결과를 제출하려면 pom.xml에 SonarQube 설정을 추가합니다. 총 3가지 방법이 있습니다.
방법 1: 데이터베이스 연결 설정
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<sonar.jdbc.url>jdbc:mysql://localhost/sonar</sonar.jdbc.url>
<sonar.jdbc.driver>com.mysql.cj.jdbc.Driver</sonar.jdbc.driver>
<sonar.jdbc.username>sonaruser</sonar.jdbc.username>
<sonar.jdbc.password>sonarpass</sonar.jdbc.password>
<sonar.host.url>http://localhost:9000</sonar.host.url>
</properties>
</profile>
</profiles>
방법 2: 사용자 이름과 비밀번호
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<sonar.host.url>http://localhost:9000</sonar.host.url>
<sonar.login>admin</sonar.login>
<sonar.password>admin123</sonar.password>
</properties>
</profile>
</profiles>
방법 3: 액세스 토큰 사용
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<sonar.host.url>http://localhost:9000</sonar.host.url>
<sonar.login>abc123def456ghi789jkl012mno345pqr</sonar.login>
</properties>
</profile>
</profiles>
위 방법 중 하나를 선택하여 설정하면 됩니다. 설정 완료 후 다음 명령어를 실행합니다:
mvn clean verify sonar:sonar
pom.xml에 설정을 추가하지 않고 명령행에서 직접 지정할 수도 있습니다:
mvn clean verify sonar:sonar -Dsonar.host.url=http://localhost:9000 -Dsonar.login=abc123def456ghi789jkl012mno345pqr
5. 최종 pom.xml 설정
전체 통합 설정이 완료된 pom.xml 예제입니다:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>SampleProject</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<skip>${maven.test.skip}</skip>
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
<output>file</output>
<append>true</append>
<excludes>
<exclude>com/example/util/PerformanceTest/**</exclude>
<exclude>com/example/config/LoadConfig</exclude>
</excludes>
<rules>
<rule implementation="org.jacoco.maven.RuleConfiguration">
<element>BUNDLE</element>
<limits>
<limit implementation="org.jacoco.report.check.Limit">
<counter>METHOD</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
<limit implementation="org.jacoco.report.check.Limit">
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
<limit implementation="org.jacoco.report.check.Limit">
<counter>CLASS</counter>
<value>MISSEDCOUNT</value>
<maximum>0</maximum>
</limit>
</limits>
</rule>
</rules>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<phase>test-compile</phase>
</execution>
<!-- check 실행을 원하면 주석 해제 -->
<!--
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
</execution>
-->
<execution>
<id>jacoco-site</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<sonar.host.url>http://localhost:9000</sonar.host.url>
<sonar.login>abc123def456ghi789jkl012mno345pqr</sonar.login>
</properties>
</profile>
</profiles>
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<jacoco.version>0.8.8</jacoco.version>
</properties>
</project>
6. 결론
JaCoCo는 프로젝트의 코드 품질 관리에 매우 중요한 도구입니다. 테스트 커버리지를 지속적으로 모니터링하면 테스트되지 않은 코드 영역을 조기에 발견하고 개선할 수 있습니다. Maven과의 원활한 통합과 SonarQube 연동을 통해 효과적인 품질 관리를 구현할 수 있습니다.