--- kind: CASE slug: analysis-finding-a18-f002 title: 실패는 환경 원인이며, 그 테스트의 도구 가드가 불완전하다 topic: composition-root-and-bootstrap project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:analysis-finding-a18-f002 evidenceCapturedOn: 2026-09-01 body: case-analysis-finding-a18-f002.body.md assets: - key: analysis-finding-a18-f002 file: ../../../final/evidence/rendered/analysis-finding-a18-f002.svg evidence: - ../../../final/evidence/raw/analysis-finding-a18-f002.txt source: - 원본 분석 절은 final/document.md#a18#L549 이다. --- # 실패는 환경 원인이며, 그 테스트의 도구 가드가 불완전하다 이 묶음의 유일한 실패는 저장소 결함이 아니다. 분석 환경에 도구 하나가 없고 위임된 스크립트가 그것을 요구하며 정직하게 실패한다. 기록하는 것은 같은 테스트가 첫째 도구는 건너뛰고 둘째 도구는 실패로 다룬다는 비대칭이다. ## 관계 - **아무것도 발견하지 못한 레인은 성공이 아니라 실패여야 한다** 이 저장소가 가진 반대 방향 원칙이다. - **초록일 수 없는 게이트는 사람들이 건너뛰는 법을 배우게 만든다** 같은 계열의 규칙이다. - **한 test 안에서 도구별로 갈리지 않는 편이 낫다** 권고의 형태다. ## 문제 부트스트랩 묶음을 돌렸다. 천열다섯 테스트가 통과하고 하나가 실패하고 넷이 건너뛰어졌다. 그 하나를 확인했다. ## 결론 저장소 결함이 아니다. 실패 메시지가 원인을 그대로 적는다. 위임된 스크립트가 도구 하나를 요구하고 없어서 종료 코드 칠십팔로 끝났다는 것이다. 분석 환경에 그 도구가 없다. 기록하는 것은 가드의 비대칭이다. 테스트는 컨테이너 도구 부재를 가정으로 처리해 건너뛴다. 같은 스크립트가 요구하는 다른 도구의 부재는 실패로 나타난다. 도구가 없는 기계에서 이 테스트는 계약 위반처럼 읽히는 실패를 낸다. 메시지가 원인을 드러내므로 오해가 오래가지는 않는다. 다만 이미 건너뛰기를 선택한 테스트가 두 번째 도구에 대해서만 다르게 행동한다. 이 저장소는 반대 방향의 원칙도 갖고 있다. 역방향 프록시 레인이 건너뛰기를 거부하며 그 이유를 적는다. 컨테이너 실행기가 없을 때 조용히 통과하는 레인은 그 실행기가 마지막으로 망가진 이래로 아무것도 인증하지 않은 레인이라는 것이다. 두 원칙 중 어느 쪽을 택하든 한 테스트 안에서 도구별로 갈리지는 않는 편이 낫다. 판정은 P3 다. ## 검증 환경 Gradle : 9.0.0 확인 방식 : 묶음 실행과 실패 메시지 확인, 도구 존재 확인 소스 수정 : x ## 재현 조건 원문은 final/evidence/raw/188 계열에 있다. 1. 부트스트랩 묶음을 실행한다. 2. 실패한 테스트와 메시지를 확인한다. 3. 위임된 스크립트가 요구하는 도구를 확인한다. 4. 그 도구가 환경에 있는지 확인한다. 5. 테스트의 가정 처리 대상을 확인한다. ## 본문 테스트 실패의 형태는 다음이다. ``` org.opentest4j.AssertionFailedError: [verify-compose-profile-contracts.sh said: jq is required ] expected: 0 but was: 78 at ComposeMergeCharacterizationTest.everyLaneMatchesItsContract(ComposeMergeCharacterizationTest.java:62) $ which jq -> NO_JQ ``` ## 테스트 실패의 형태 :::evidence key="analysis-finding-a18-f002" alt="분석 문서 final/document.md#a18 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md#a18 발췌 — 15줄" zoom="true" ::: ## 기록하는 것은 가드의 비대칭이다 ```java @Test void everyLaneMatchesItsContract() { Assumptions.assumeTrue(dockerComposeIsAvailable(), "docker compose is not on this machine"); ProcessResult result = run(List.of("./scripts/verify-compose-profile-contracts.sh")); assertThat(result.exitCode()).isZero(); } ``` docker compose는 가정으로 확인하고 `jq`는 확인하지 않는다. P3. ## 확인하지 못한 것 없는 도구를 설치하지 않았다. 분석 환경을 바꾸지 않는다는 원칙 때문이다. 계약 일치 자체는 독립 구현으로 따로 확인했다. 저장소 결함이 아니다. 분석 컨테이너에 jq가 없고, 위임된 스크립트가 그것을 요구하며 exit 78로 정직하게 실패한다.