본문 바로가기
빌드노트 검색

빌드노트

테스트는 컴파일됐는데 mvn test에서 빠지는 이유

Maven 테스트 컴파일 성공은 Surefire의 실행 대상 포함을 보장하지 않는다. 기본 클래스명 패턴에서 빠진 테스트를 -Dtest로 확인하고, includes 교체 시 기존 패턴을 유지하는 방법과 테스트 0개 실패 옵션의 한계를 실제 실행으로 비교한다.

mvn test는 성공했는데 새로 만든 JUnit 테스트 클래스의 실행 기록이 없을 수 있다. 테스트 소스를 컴파일하는 단계와 Surefire가 실행할 클래스를 고르는 단계가 다르기 때문이다. ArithmeticCheck.java는 컴파일돼도 Surefire의 기본 이름 패턴에 맞지 않아 실행 대상에서 빠진다. @Test가 있다는 것만으로 일반 mvn test의 실행 대상이 되지는 않는다.

내 클래스만 지정해서 실행해 본다

먼저 문제가 있는 모듈의 pom.xml이 있는 개발용 폴더에서 확인한다. 아래는 JDK와 Maven이 설치되고 JUnit 테스트 의존성이 설정된 Windows PowerShell 기준이다. 예시 대상은 src/test/java/example/ArithmeticCheck.java이며, 파일명과 패키지는 자신의 클래스에 맞게 바꾼다. 다른 셸에서는 mvn.cmd 대신 mvn을 사용할 수 있다.

다음 두 명령은 새로 컴파일한 결과에 대상 클래스가 있는지 확인한다. clean은 해당 모듈의 이전 빌드 산출물을 지우므로, 예전 .class 파일을 이번 컴파일 결과로 착각하는 것을 피할 수 있다.

mvn.cmd -B clean test
Get-Item .\target\test-classes\example\ArithmeticCheck.class

첫 명령이 의존성 해석이나 testCompile 오류로 실패했다면 그 오류를 먼저 해결한다. 첫 명령이 성공하고 두 번째 명령이 클래스 파일을 보여 주는데도 실행 보고서에 이름이 없다면, 컴파일 이후의 선택 조건을 살펴볼 차례다. target/test-classes는 이 예제의 기본 출력 위치이며, 테스트 출력 경로를 바꾼 프로젝트는 그 위치를 확인한다.

이제 같은 클래스 하나를 명시해 실행 가능 여부를 확인한다. 속성 전체를 따옴표로 감싸 PowerShell에 전달한다.

mvn.cmd -B "-Dtest=ArithmeticCheck" test

별도 최소 프로젝트에서는 이 명령으로 다음 출력이 나왔다. 실행 시간 표시는 생략했다.

[INFO] Running example.ArithmeticCheck
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

Surefire 3.5.4의 test 속성은 includes와 excludes를 덮어써 대상을 선택한다. 따라서 명시 실행은 되는데 기본 실행에서 빠진다면 클래스명과 include/exclude 설정을 우선 비교할 근거가 된다. 이 명령으로 한 번 통과했다고 다음 일반 실행의 탐색 조건까지 수정된 것은 아니다. Surefire 3.5.4의 test 매개변수 (새 탭에서 열림)

명시 실행도 안 된다면 오류 내용을 나눈다. 실험에서 클래스명을 ArithmeticChek로 잘못 지정하자 No tests matching pattern "ArithmeticChek" were executed!와 종료 코드 1이 나왔다. 이름과 대상 모듈을 먼저 확인할 상황이다. 이름이 맞는데도 실행되지 않으면 JUnit 애너테이션, 실행 엔진과 필터 등 다른 조건을 살펴야 한다. Surefire의 JUnit Platform 설정 문서 (새 탭에서 열림)는 테스트 의존성과 실행 설정을 설명한다.

두 클래스가 컴파일됐지만 하나만 실행됐다

일반적인 JAR 프로젝트에서 Maven은 compiler:testCompile로 테스트 소스를 컴파일한 뒤 surefire:test를 실행한다. Maven 생명주기 문서 (새 탭에서 열림)에서 두 목표의 연결 단계를 확인할 수 있다.

Surefire 3.5.4가 기본으로 포함하는 이름 패턴은 다음 네 가지다. 패키지 경로 아래의 클래스에도 적용된다.

  • **/Test*.java
  • **/*Test.java
  • **/*Tests.java
  • **/*TestCase.java

그래서 ArithmeticTest.java와 ArithmeticTests.java는 기본 패턴에 맞지만 ArithmeticCheck.java는 맞지 않는다. 문서가 .java 형태로 패턴을 표기한다고 해서 이것이 Java 컴파일 대상 목록이라는 뜻은 아니다. Surefire의 실행 대상 선택 규칙이다. Surefire 3.5.4의 포함·제외 규칙 (새 탭에서 열림)

이를 확인하려고 업무 코드와 분리한 작은 프로젝트를 만들었다. Windows 11, Maven 3.9.16, OpenJDK 17.0.2에서 Compiler Plugin 3.14.1, Surefire 3.5.4, JUnit Jupiter 5.12.2를 사용했다. ArithmeticTest와 ArithmeticCheck에 각각 @Test 메서드 하나를 두고 2 + 2의 결과가 4인지 실제 단언했다. 각 비교 전에 빌드 산출물을 지웠다.

  • 기본 탐색: 두 .class 파일이 모두 생겼지만 실행 보고서는 ArithmeticTest의 테스트 1개뿐이었다.
  • ArithmeticCheck만 명시: 같은 소스에서 -Dtest=ArithmeticCheck로 테스트 1개가 통과했다.
  • Check 클래스만 있는 소스 구성: 클래스는 컴파일됐지만 실행 보고서는 없었고 빌드는 종료 코드 0으로 끝났다.

마지막 경우 로그에는 Tests run: 0이라는 줄도 없었다. 테스트 소스 1개를 컴파일한 기록과 Surefire 목표, BUILD SUCCESS만 이어졌다. 성공 문구만 보면 실제 테스트가 전부 빠진 것을 놓칠 수 있었다.

명시 선택된 테스트의 기대값을 5로 바꾼 비교에서는 expected: <5> but was: <4>, Tests run: 1, Failures: 1과 종료 코드 1을 확인했다. 단순히 클래스 이름만 출력된 것이 아니라 테스트 메서드의 단언까지 실행된 결과다.

다음 mvn test에서도 찾게 고친다

기존 프로젝트가 기본 이름 규칙을 쓰기로 했다면 파일과 클래스 이름을 ArithmeticTest처럼 맞추는 방법이 가장 작다. 이름이 바뀐 뒤 생략 옵션이나 -Dtest 없이 일반 mvn test를 실행해 해당 클래스의 보고서가 생기는지 확인한다.

프로젝트가 의도적으로 *Check 이름을 쓴다면 Surefire에 포함 규칙을 설정한다. 사용자가 지정한 includes는 기본 네 패턴에 자동으로 덧붙는 목록이 아니다. 실험에서 **/*Check.java만 설정하자 이번에는 ArithmeticCheck만 실행되고 기존 ArithmeticTest가 빠졌다.

기본 이름 네 종류와 *Check를 모두 유지하려면 pom.xml의 build/plugins 아래 maven-surefire-plugin에 있는 configuration 안에 다음 includes를 설정한다. 이 비교에서 사용한 플러그인 버전은 3.5.4다.

<includes>
  <include>**/Test*.java</include>
  <include>**/*Test.java</include>
  <include>**/*Tests.java</include>
  <include>**/*TestCase.java</include>
  <include>**/*Check.java</include>
</includes>

실험에서는 이 다섯 패턴을 설정한 일반 실행에서 두 클래스의 테스트가 각각 1개씩, 합계 2개 통과했다. 현재 POM에 부모 설정이나 활성 프로필의 include/exclude가 있다면 함께 확인한다. 특히 명시 실행이 통과해도 기본 실행에는 제외 규칙이 다시 적용될 수 있다.

테스트 0개를 성공으로 넘기지 않으려면

테스트가 있어야 하는 모듈에서는 다음 명령으로 실행 대상이 전혀 없는 경우를 실패로 만들 수 있다.

mvn.cmd -B "-DfailIfNoTests=true" test

Check 클래스만 있고 기본 탐색을 쓰는 실험에 이 옵션을 붙이자 No tests were executed!와 종료 코드 1이 나왔다. Surefire 3.5.4에서 failIfNoTests의 기본값은 false다. 명시한 -Dtest가 아무것도 찾지 못할 때 쓰는 failIfNoSpecifiedTests는 기본값이 true인 별도 조건이다. 두 실패 조건의 매개변수 설명 (새 탭에서 열림)

다만 이 옵션은 일부 클래스 누락까지 잡아 주지 않는다. ArithmeticTest 하나라도 실행되면 전체 실행 수는 0이 아니기 때문이다. 누락된 클래스 이름이 실행 로그와 target/surefire-reports의 이번 보고서에 실제로 있는지까지 확인해야 한다.

위 관찰은 고정한 버전의 단일 모듈 JUnit Jupiter 프로젝트에서 확인했다. IDE의 탐색 규칙, Failsafe 통합 테스트, 중첩 테스트 클래스와 다중 모듈 실행은 검증하지 않았다. 자기 결과가 다르면 먼저 로그의 Surefire 버전과 실제 적용된 필터를 대조한다.

← 전체 글 보기