[Android] 안드로이드 스튜디오에 Jacoco 추가하기

Minji Jeong·2023년 6월 24일
0

Troubleshooting

목록 보기
19/20
post-thumbnail

몇달 전 회사에서 개발중인 안드로이드 프로젝트에 대한 CI/CD 환경을 구성했고, 그에 따라 앱에 대한 테스트 코드 커버리지를 알 수 있도록 JaCoCo를 적용해보자는 의견이 나왔다. JaCoCo라는 것에 대해서는 이번에 처음 알게 되었는데, 팀에서 이 툴을 사용해서 코드 커버리지를 확인해보자고 해서 그 존재를 알게되었다. 일단 이 글을 쓰는 이유는 JaCoCo를 프로젝트에 적용하면서 약간의 삽질을 했기 때문에 🔨😡 이 과정을 공유하고자 글을 쓰게 되었다.

그런데 글을 쓰면서 ‘JaCoCo를 설정하다가 이런 오류가 나서 이렇게 해결했습니다~’만 설명하기에는, 내가 모르는 것들이 많았다. 그동안 혼자서 토이프로젝트를 개발했을 때는 사실 테스트 과정의 중요성을 느끼지 못해서 그냥 기능을 완성할 때마다 정상적으로 실행되는지 빌드만 했었는데, 이번 기회를 계기로 삼아 왜 JaCoCo를 써야하는지, 코드 커버리지는 무엇이고 왜 필요한지, 더 나아가 소프트웨어 개발에서 테스팅이 얼마나 중요한지 등등에 대해서도 조사를 해서 글에 살짝 녹여보기로 했다.

먼저, 소프트웨어 테스팅이란 무엇인가?

Software Testing

사실 말 그대로다. 응용 프로그램 또는 시스템의 동작과 성능, 안정성이 사용자가 요구하는 수준을 만족하는지 확인하기 위해 발견하는 과정이다.

그렇다면 결함은 어떻게 일어날까?

ISTQB에서는 소프트웨어 결함이 다음의 과정을 통해 일어난다고 언급하고 있다.

인간은 코드, 소프트웨어, 시스템, 또는 문서 안에 결함을 만들어내는 오류(실수)를 범할 수 있으며, 결함 코드가 실행되면 시스템은 바라던 결과에 대해 실패하거나, 바라지 않던 결과로 인해 실패할 수 있다.

인간이 개발하는 한, 버그없는 소프트웨어는 존재하지 않는다. 특정 환경에서는 문제없이 정상적으로 작동하던 소프트웨어라 해도, 다른 환경에서는 실패할 수도 있다.

이러한 결함들을 수정하는 비용은 작을수도 있지만, 클 경우에는 막대한 비즈니스 리스크를 발생시킬 수도 있다. 그리고 그 결함을 수정하기 위해서 개발자들의 많은 노력과 시간이 소모된다. 결론적으로는

  • 이러한 결함들을 사전에 예방하고, 소프트웨어의 품질을 향상시키기 위해
  • 소프트웨어가 적절히 동작하는지 확인하기 위해
  • 소프트웨어의 품질 수준에 대한 자신감 획득과 정보를 제공하기 위해
  • 소프트웨어가 사용자 및 비즈니스를 충족하였는지 확인하기 위해

우리는 테스팅을 수행해야 한다.

그리고 우리가 수행한 테스팅이 충분한지에 대한 지표가 바로 코드 커버리지이다.

💡 Code Coverage

테스트 케이스가 얼마나 충분한가를 나타내는 지표로, 테스트를 진행하였을 때 코드 자체가 얼마나 실행되었는지에 대한 수치

개발자가 작성한 테스트 코드를 실행시켜보고, 해당 테스트 코드가 작성된 코드를 얼마만큼 검증했는지를 수치로 나타낸 것이다.

우리는 JaCoCo를 사용해서 작성한 소스코드의 코드 커버리지를 측정한 후, 이를 html 또는 xml 형식의 시각적인 파일로 확인할 수 있다.

JaCoCo (Java Code Coverage)

  • 소스코드의 코드 커버리지를 측정한 후, 이를 시각적인 파일로 보여주는 오픈소스
  • 자바 코드에 대한 커버리지를 측정한다고 하지만, 0.8.5 버전 이상에서는 큰 설정 없이 코틀린 코드에 대해서도 커버리지 측정 가능

이제 소프트웨어 테스팅을 수행해야 하는 이유와 코드 커버리지 및 JaCoCo에 대한 개념에 대해 알았으니, 이제 안드로이드 스튜디오에 JaCoCo를 적용해보자. 여기서부터는 적용 과정에서 겪은 삽질에 대한 내용이다.

일단 JaCoCo 설정은 해당 블로그와 그래들 문서를 참고하여 진행했으며,

👉 Jacoco를 사용하여 코드 커버리지 확인하기
👉 Gradle : The JaCoCo Plugin

나는 JaCoCo 0.8.5 버전을 적용해보기로 했다.

build.gradle (app)

plugins {
	id 'jacoco' // JaCoCo 플러그인 적용
}

android {
	...
	testOptions {
        unitTests.includeAndroidResources = true
        unitTests.returnDefaultValues = true

        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
                jacoco.excludes = ['jdk.internal.*']
            }
        }
  }
	...
}

jacoco {
    toolVersion = "0.8.5" // JaCoCo 버전 설정
}

task jacocoTestReport(type: JacocoReport, dependsOn: ['testFieldDebugUnitTest']) {

    def mainSrc = "${project.projectDir}/src/main/java"
    sourceDirectories.setFrom(files([mainSrc])) // 커버리지를 측정하고자 하는 디렉터리

    // 커버리지에서 제외할 파일들
    def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
    def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
    classDirectories.setFrom(files([debugTree])) // 컴파일 결과 파일이 있는 디렉터리

    println "${buildDir}"

    // 커버리지 측정 결과를 저장할 파일
    executionData.setFrom(fileTree(dir: "${buildDir}/jacoco/testDebugUnitTest.exec"))
}

이렇게 build.gradle(app)에 작성을 해주고, 커버리지를 측정하기 위해 터미널에서 다음의 명령어를 입력했으나,

./gradlew testFieldDebugUnitTest

다음과 같이 오류가 발생했다.

* What went wrong:
Could not initialize class org.codehaus.groovy.runtime.InvokerHelper
> Exception java.lang.NoClassDefFoundError: Could not initialize class org.codehaus.groovy.reflection.ReflectionCache [in thread "Daemon worker"]

역시, 쉽게 넘어가는 법이 없다.
일단 could not initialize class org.codehaus.groovy.runtime.InvokerHelper 이 부분을 구글에 검색해보았다.

👉 gradlew org.codehaus.groovy.runtime.InvokerHelper 문제 해결

그래들 버전에 따라 지원되는 자바의 버전이 다르다고 한다. 그래들 문서그래들 6.7.1 릴리즈 노트에서 각 그래들 버전마다 호환되는 자바 버전을 확인해봤는데,

6.7.1은 자바 15를 지원한다.

내 안드로이드 스튜디오에서 사용중인 그래들 버전은 6.7.1 이었고, 시스템에 설정된 자바 버전은 17이었다.

☑️ Android Studio에서의 Gradle 버전 확인 방법 (MacOS 기준)

  • gradle-wrapper.properties 파일 확인
  • 상단 메뉴에서 File -> Project Structure에서 확인

☑️ 시스템에 설정된 Java 버전 확인 방법

터미널에 java -version 입력

바로 시스템에 설정된 자바 버전을 15로 다운그레이드했다.

export JAVA_HOME=$(/usr/libexec/java_home -v {JAVA_VERSION})

이렇게 변경해주고 다시 실행했더니

응 안돼

근데 알고보니 이 오류는 나의 단순한 실수로 인해 발생한 것이었다. 나는 이 명령어 ./gradlew testFieldDebugUnitTest를 루트 하위에 app 모듈에서 실행했었는데(왜그랬을까? ㅎ) 다시 루트로 이동해서 이 명령어를 실행해보니 이 오류는 발생하지 않았다. 다만 이제는

'include_flutter.groovy' unsupported class file major version 62

위 오류가 발생했다. 하위에 구성된 플러터 모듈과 관련된 오류일까. 역시 검색을 해본다. 구글에 검색했더니 글이 많은 걸 봐서 많은 사람들을 힘들게 한 에러인 듯 했다.

👉 StackOverFlow : Android Studio Error "Unsupported class file major version 61"

요 글을 참고해서 자바의 버전을 11로 낮추었다.

성공했다.

일단 InvokerHelper는 명령어를 실행하는 디렉터리 경로가 잘못된, 단순한 나의 실수로 인한 오류였다. 근데 여기서 좀 이상한 것이, 그래들 버전 6.7.1은 자바 15를 지원하며, 내 로컬 컴퓨터에 설정된 자바 버전은 15로 맞게 설정해줬음에도 불구하고, 왜 빌드가 안되는지 의문이 들었다.

그래서 지금껏 했던 삽질들을 되돌아보며 JaCoCo 0.8.5 버전의 릴리즈 노트를 확인했다.

0.8.5 버전은 자바 14까지만 지원이 되었기에, 15 17 버전에서는 빌드가 되지 않았던 것이다. 따라서 위 릴리즈 노트를 참고해서 JaCoCo 버전에 맞게 시스템 자바 버전을 맞춰주면 해결되는 문제였다.

References

https://sw-test.tistory.com/4
https://softwaretestingreference.tistory.com/137
https://heegs.tistory.com/131
https://docs.gradle.org/current/userguide/jacoco_plugin.html
https://epicarts.tistory.com/109
https://docs.gradle.org/current/userguide/compatibility.html
https://stackoverflow.com/questions/70632741/android-studio-error-unsupported-class-file-major-version-61
https://www.jacoco.org/jacoco/trunk/doc/changes.html

profile
Mobile Software Engineer

0개의 댓글