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

빌드노트

실패한 IT가 있어도 mvn integration-test가 성공하는 이유

Failsafe는 통합 테스트 실행과 실패 판정을 나눈다. 실패 IT에서 integration-test는 종료 0과 후처리 누락, verify는 종료 1과 후처리 실행을 보인 실제 비교를 바탕으로 두 goal의 바인딩과 CI의 마지막 lifecycle 단계를 확인한다.

CI가 mvn integration-test를 호출하는데 통합 테스트(IT)의 실패 로그 뒤에 BUILD SUCCESS가 나온다면, Failsafe의 실패 판정 단계까지 실행했는지 확인해야 한다. Failsafe는 테스트를 실행하는 goal과 결과를 검사하는 goal을 나눈다. 두 goal이 정상적으로 바인딩된 프로젝트에서는 mvn verify까지 호출해야 후처리를 거쳐 통합 테스트 실패를 빌드 실패로 판정한다.

integration-test에서 멈추면 그 뒤의 post-integration-test도 실행하지 않는다. 여기에 서버 종료 같은 작업을 연결했다면 정리까지 빠질 수 있다. 이 글은 실제 CI 장애 기록이 아니라, 서버 없이 실패하는 테스트와 파일 marker로 이 차이를 확인한 별도 실험이다.

CI 명령과 Failsafe의 두 바인딩부터 본다

먼저 CI가 호출하는 마지막 lifecycle 단계와 해당 모듈의 pom.xml을 함께 본다. verify라는 단계 이름만 호출해도 Failsafe가 자동으로 추가되는 것은 아니다. 실제 빌드에서 활성화되는 build/plugins 설정에 테스트 실행과 결과 검사가 모두 연결돼 있어야 한다.

아래는 확인할 두 바인딩이다. 이미 Failsafe를 선언했다면 기존 plugin의 executions에서 확인한다. 3.5.4는 이번 실험에 고정한 버전이며, 자신의 프로젝트에서는 사용 중인 버전의 설정과 대조한다.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>3.5.4</version>
  <executions>
    <execution>
      <id>run-it</id>
      <phase>integration-test</phase>
      <goals><goal>integration-test</goal></goals>
    </execution>
    <execution>
      <id>check-it</id>
      <phase>verify</phase>
      <goals><goal>verify</goal></goals>
    </execution>
  </executions>
</plugin>

failsafe:integration-test가 테스트를 실행하고, failsafe:verify가 그 결과를 검사한다. 그 사이에 놓인 post-integration-test의 정리 작업은 서버나 컨테이너를 관리하는 플러그인에서 별도로 연결해야 한다. Failsafe를 추가하는 것만으로 종료 작업이 생기지는 않는다. Failsafe 사용 설정 (새 탭에서 열림)과 두 goal의 역할 (새 탭에서 열림)을 확인할 수 있다.

두 바인딩이 있고 IT가 실행되는 모듈이라면, CI가 integration-test에서 끝나지 않도록 최종 단계를 verify로 바꾼다. Maven lifecycle 문서 (새 탭에서 열림)에 따르면 지정한 단계까지 앞 단계를 순서대로 실행하므로 pre-integration-test, integration-test, post-integration-test를 명령에 나열할 필요가 없다.

다음은 Maven과 프로젝트에 필요한 JDK가 준비된 Windows PowerShell에서, 그 모듈의 pom.xml이 있는 디렉터리에서 실행하는 로컬 진단 명령이다. Maven 종료 코드와 이번 실행의 summary를 함께 확인한다. 필요한 IT 프로파일이 있다면 평소 CI와 같은 프로파일을 적용한다.

mvn.cmd -B clean verify
"Maven exit=$LASTEXITCODE"
Get-Content ./target/failsafe-reports/failsafe-summary.xml

clean은 이전 보고서를 지워 이번 실행과 섞이지 않게 하기 위한 선택이다. 단계 누락을 고치는 부분은 verify다. 위의 출력 명령은 로컬 진단용이며, CI에서는 Maven의 종료 코드를 그대로 작업 실패에 반영해야 한다. 보고서 조회 명령의 성공으로 Maven 실패 코드를 덮어쓰면 안 된다. 보고서 경로를 재설정한 프로젝트라면 그 경로를 읽는다.

실패는 같았고, 명령의 끝만 달랐다

새 독립 Maven 프로젝트에 JUnit 4.13.2 테스트 LifecycleIT 하나를 두었다. 테스트는 marker를 한 줄 쓴 뒤 의도적으로 단언에 실패한다. pre-integration-test와 post-integration-test에는 Antrun 3.1.0으로 같은 파일에 단계 이름을 덧붙이게 했다. 서버 기동·종료 대신 단계 도달 여부만 관찰한 장치다.

환경은 Windows 11, Maven 3.9.16, Java 17.0.2였고 Failsafe와 Surefire는 각각 3.5.4로 고정했다. Surefire는 기본 test 단계에 두고, 이 IT는 Failsafe에서만 실행된 것을 보고서로 확인했다. 모든 비교는 새로 clean한 뒤 실행했다.

  • 실패하는 IT로 clean integration-test: 테스트 1개 중 실패 1개였지만 Maven 종료 코드는 0, 마지막 빌드 판정은 BUILD SUCCESS였다. marker는 pre-integration-test → integration-test에서 끝났다.
  • 같은 실패 IT로 clean verify: 테스트 1개 중 실패 1개, 종료 코드는 1이었다. marker에 post-integration-test까지 기록됐고, 그 뒤 failsafe:verify에서 BUILD FAILURE가 나왔다.
  • 단언이 통과하도록 바꾼 IT로 clean verify: 테스트 1개 중 실패 0개, 종료 코드는 0이었다. 전처리·테스트·후처리 marker가 모두 기록됐고 검증 goal도 실행됐다.

첫 실행에서 실제 Maven 로그의 테스트 집계와 최종 판정은 다음처럼 달랐다. 중간 구분선은 생략했다.

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

이는 Failsafe가 단언 실패를 놓쳤다는 뜻이 아니다. 실패는 이미 기록됐지만, 호출한 lifecycle이 그 결과를 최종 판정하는 단계에 도달하지 않은 것이다. Failsafe 소개 (새 탭에서 열림)는 이런 분리 덕분에 테스트 실패 뒤에도 후처리를 실행할 수 있다고 설명한다.

보고서가 있어도 검증까지 끝난 것은 아니다

실험에서는 integration-test만 실행했을 때도 target/failsafe-reports에 TEST-lab.LifecycleIT.xml, lab.LifecycleIT.txt, failsafe-summary.xml이 모두 생성됐다. summary의 completed는 1, failures도 1이었다. 보고서 생성과 Maven의 최종 실패 판정은 별도로 확인해야 한다.

failsafe:verify는 기본적으로 이 summary 파일을 읽어 결과를 검사한다. 테스트를 다시 실행하는 goal이 아니다. 실패한 verify 실행의 로그에서는 전처리 Antrun → Failsafe 테스트 실행 → 후처리 Antrun → Failsafe 검증 순서가 확인됐다. 테스트 marker도 한 번만 남았다. 검증 goal의 summaryFile 설명 (새 탭에서 열림)

따라서 확인할 것은 보고서 파일의 존재뿐 아니라 이번 IT의 실행 건수, post-integration-test에 연결한 작업의 완료, 마지막 failsafe:verify 실행과 Maven 종료 코드다. verify를 호출했는데 Failsafe goal이 로그에 없거나 IT가 0개라면, 활성 프로파일과 실행 바인딩·테스트 선택부터 확인해야 한다. 그 상태의 종료 코드 0은 IT 통과 근거가 되지 않는다.

검증 goal까지 실행됐고 summary에 실패가 남아 있는데도 Maven이 0을 반환한다면 결과 무시 설정도 확인한다. 공식 검증 goal 문서의 testFailureIgnore와 대응 속성 maven.test.failure.ignore가 그 대상이다. 이번 실험에는 이 설정을 넣지 않았다. Maven은 1을 반환했는데 CI만 성공이라면 CI 스크립트가 그 종료 코드를 보존하는지 확인한다.

mvn failsafe:verify처럼 goal 하나를 직접 호출하는 것도 mvn verify와 다르다. 전자는 앞선 lifecycle 단계들을 진행시키지 않는다. 이미 만들어진 결과를 따로 검사하려는 경우와, 준비부터 후처리·검증까지 실행하려는 CI 호출을 구분한다. Maven의 phase와 goal 실행 규칙 (새 탭에서 열림)

여기서 증명한 것은 Failsafe 3.5.4에서 보통의 테스트 단언 실패가 후처리를 거친 뒤 빌드 실패로 반영되는 순서다. 확인 당시 공식 온라인 goal 문서는 3.6.0을 표시했으며, 위 수치는 3.5.4의 실제 실행 결과다. 실제 서버 정지, 준비 단계의 오류, JVM 비정상 종료나 CI 강제 취소는 검증하지 않았다. post-integration-test를 모든 중단 상황의 정리 보장으로 해석할 수는 없다.

← 전체 글 보기