테마 선택

📚 지식 노트

AI·하드웨어·주변기기·게임·컴퓨터 시스템·이론 컴퓨터 과학·수학·개발과 운영·보안·응용 분야에 대해 알아 두면 좋은 내용을 정리한 노트다. 위에서 분야를 고르고 아래에서 노트를 고른다.

소프트웨어 테스트

어떤 테스트를 얼마나 둘지(피라미드와 트로피), 단위·통합·E2E가 어디까지를 진짜로 쓰는지, 목과 스텁의 차이, 커버리지 100%가 버그 없음을 뜻하지 않는 이유, 속성 기반 테스트와 뮤테이션 테스트로 테스트 자체를 시험하는 법, 흔들리는 테스트, TDD와 계약 테스트까지를 작은 예제로 직접 계산해 가며 정리한다.

목차 (11)
  1. 테스트 피라미드와 트로피
  2. 단위, 통합, E2E
  3. 테스트 한 개의 모양: 준비, 실행, 확인
  4. 테스트 더블: 목, 스텁, 페이크
  5. 커버리지와 그 한계
  6. 속성 기반 테스트
  7. 뮤테이션 테스트: 테스트를 시험하기
  8. 불안정한 테스트
  9. TDD: 테스트를 먼저
  10. 계약 테스트
  11. 핵심 자료

테스트는 ‘코드가 맞다’를 증명하지 않는다. 테스트가 하는 일은 바꿔도 된다는 자신감을 주는 것이다. 좋은 테스트 묶음은 무언가 깨졌을 때 빨리, 정확한 자리를 가리키며 실패하고, 아무것도 깨지지 않았을 때는 조용하다. 이 테스트들이 매 커밋마다 도는 곳이 CI/CD 노트의 파이프라인이고, 성능을 재는 테스트는 부하 테스트 노트에서 따로 다룬다.

테스트 피라미드와 트로피

마이크 콘(Mike Cohn)의 테스트 피라미드는 빠르고 싼 단위 테스트를 많이, 느리고 비싼 E2E 테스트를 적게 두라는 그림이다. 예제 묶음에서 E2E 30개는 600초가 걸리지만 단위 3,000개는 15초면 끝난다. 개수는 100분의 1인데 시간은 40배다. 통합 테스트 300개는 그 사이다.

테스트 피라미드와 테스팅 트로피: 피라미드는 아래 단위 테스트가 가장 많고 위 E2E가 가장 적으며, 트로피는 통합 테스트를 가장 두껍게 두고 맨 아래에 정적 분석을 깐다테스트 피라미드(Cohn)테스팅 트로피(Dodds)E2E 30개개당 20초 · 합 600초통합 300개개당 0.5초 · 합 150초단위 3,000개개당 0.005초 · 합 15초E2E통합단위정적 분석위로 갈수록 느리고 비싸지만 실제 사용에 가깝다. 색칠한 층을 가장 두껍게 둔다.
두 모델. 피라미드는 실행 비용을 기준으로, 트로피는 ‘사용자가 쓰는 방식과 얼마나 닮았나’ 대비 비용을 기준으로 층의 두께를 정한다.

켄트 C. 도즈(Kent C. Dodds)의 테스팅 트로피는 프런트엔드 경험에서 나왔다. 컴포넌트 내부를 잘게 목으로 바꾼 단위 테스트는 리팩터링할 때마다 깨지면서도 실제 버그는 놓치기 쉬우니, 여러 부품을 함께 쓰는 통합 테스트를 가장 두껍게 두고 오타·타입 오류는 정적 분석(타입 검사, 린트)에 맡기자는 것이다. 두 모델은 대립이라기보다 ‘구현이 아닌 동작을 시험하라, 느린 테스트는 아껴 쓰라’는 같은 원칙의 다른 그림이다.

단위, 통합, E2E

세 이름의 경계는 팀마다 조금씩 다르지만, 핵심 질문은 같다. 이 테스트는 시스템의 어디까지를 진짜로 쓰는가? 진짜로 쓰는 범위가 넓을수록 실제 버그를 더 잡지만 느려지고, 실패했을 때 원인을 찾기 어렵고, 환경 문제로 흔들리기 쉽다.

테스트 범위: 단위 테스트는 서비스 코드만 진짜로 쓰고 DB와 외부 결제는 가짜로, 통합 테스트는 API·서비스·DB를 진짜로, E2E는 화면부터 DB까지 진짜로 쓰고 외부 결제만 샌드박스로 바꾼다화면API서비스DB외부 결제단위진짜가짜가짜통합진짜진짜진짜가짜E2E진짜진짜진짜진짜가짜통합 테스트의 DB는 컨테이너로 띄운 진짜 DB다.외부 결제처럼 남의 시스템은 E2E에서도 샌드박스(가짜)로 바꾼다.
주문 서비스를 예로 든 세 테스트의 범위. 칠한 칸은 진짜, 점선 칸은 테스트용 가짜다.
  • 단위: 함수·클래스 하나를 다른 부품과 떼어 시험한다. 수 밀리초에 끝나 수천 개를 매 저장마다 돌릴 수 있다. 계산·분기가 많은 순수한 로직에 가장 잘 맞는다.
  • 통합: 내 코드와 DB·메시지 큐 같은 진짜 의존성을 함께 시험한다. SQL이 실제 DB에서 도는지, 직렬화가 맞는지는 여기서만 확인된다. Testcontainers처럼 테스트마다 컨테이너로 DB를 띄우면 메모리 DB 같은 흉내보다 믿을 만하다.
  • E2E: 사용자처럼 화면에서 시작해 전체를 지난다. Playwright 같은 도구로 브라우저를 조종한다. 가입·결제처럼 돈이 걸린 핵심 경로 몇 개에만 두고, 경우의 수는 아래층에서 다룬다.

테스트 한 개의 모양: 준비, 실행, 확인

테스트 하나는 세 부분으로 읽힌다. 시험 전의 상태를 만드는 준비, 시험할 동작 하나를 부르는 실행, 결과를 보는 확인이다. 같은 구조를 Arrange-Act-Assert(빌 웨이크), Given-When-Then(행동 주도 개발), 정리 단계를 더한 4단계 테스트(메스자로스)라고 부른다(GivenWhenThen). 이름이 달라도 요점은 같다. 실행이 한 번뿐이어야 실패했을 때 무엇이 깨졌는지 바로 안다.

  • 이름에 기대 동작을 적는다: test1 대신 “재고가 0이면 주문을 거절한다”처럼 쓰면, 실패 보고만 읽어도 어떤 약속이 깨졌는지 보인다.
  • 테스트끼리 기대지 않는다: 앞 테스트가 남긴 DB 행이나 전역 변수에 기대면 순서를 바꾸거나 병렬로 돌릴 때 깨진다. 준비는 테스트마다 따로 한다.
  • 버그를 고칠 때는 그 버그부터 테스트로: 고치기 전에 실패하는 테스트를 하나 남기면 같은 버그가 다시 들어오는 것(회귀)을 막는다.
  • 스냅숏 테스트: 출력 전체를 파일로 저장해 두고 다음 실행과 비교하는 방식이다. 만들기는 쉽지만, 실패할 때마다 원인을 보지 않고 jest --updateSnapshot으로 다시 찍는 습관이 들면 아무것도 검사하지 않게 된다. Jest 문서도 스냅숏을 코드처럼 리뷰하고, 짧고 초점 있게 유지하라고 권한다(Snapshot Testing).

테스트 더블: 목, 스텁, 페이크

시험 대상이 기대는 부품을 테스트용으로 바꿔 끼운 것을 통틀어 테스트 더블(test double, 대역 배우에서 따온 말)이라 한다. 제라드 메스자로스(Gerard Meszaros)가 정리한 다섯 가지가 표준 용어다. 흔히 모두 ‘목’이라 부르지만, 무엇을 검증하느냐가 다르다.

테스트 더블 다섯 가지: 더미는 자리만 채우고, 스텁은 정해 둔 답을 주고, 스파이는 호출을 기록하고, 목은 기대한 호출을 검증하며, 페이크는 메모리 DB처럼 가볍게 실제로 동작한다더미자리만 채운다쓰이지 않는 인자스텁정해 둔 답을돌려준다스파이스텁 + 어떻게불렸는지 기록목기대한 호출이왔는지 검증페이크가볍게 진짜처럼동작(메모리 DB)상태 검증결과 값을 본다(스텁·페이크)행위 검증어떤 호출을 했는지 본다(스파이·목)
테스트 더블 다섯 가지. 왼쪽은 시험 대상에게 입력을 주는 쪽, 오른쪽으로 갈수록 시험 대상이 밖으로 한 일을 지켜보는 쪽이다.
  • 상태 검증: 스텁이 ‘환율은 1,300원’이라고 답하게 해 두고, 결과 금액이 맞는지 본다. 구현이 바뀌어도 결과가 같으면 테스트가 깨지지 않는다.
  • 행위 검증: 목이 ‘이메일 발송이 정확히 한 번, 이 주소로 불렸는가’를 확인한다. 밖으로 나가는 부수 효과(메일, 결제 요청)는 결과 값이 없으니 행위로 볼 수밖에 없다.
  • 목을 과하게 쓰면: 내부 호출 순서까지 목으로 고정하면 테스트가 구현의 사본이 된다. 리팩터링마다 깨지고, 진짜 부품과의 약속이 바뀌어도 목은 옛 약속대로 답하니 통과한다. 내가 소유하지 않은 경계(외부 API)는 목으로, 내가 소유한 협력 객체는 가능하면 진짜나 페이크로 쓰는 것이 무난하다(Mocks Aren't Stubs).

커버리지와 그 한계

커버리지는 테스트가 코드의 몇 %를 실행했는지다. 줄 커버리지는 실행된 줄, 분기 커버리지는 각 if의 참·거짓이 모두 나왔는지를 센다. 커버리지가 낮으면 ‘시험받지 않은 코드가 있다’는 확실한 신호다. 그런데 높다고 ‘버그가 없다’는 뜻은 아니다. 커버리지는 코드가 실행됐는지만 볼 뿐, 그 결과를 확인했는지, 어떤 입력으로 실행했는지는 보지 않는다.

커버리지 100%의 함정: average 함수의 실행 가능한 줄이 모두 테스트로 실행되어 줄·분기 커버리지가 100%지만, 0만 들어오거나 빈 배열이면 0을 0으로 나눠 NaN을 돌려준다1function average(values, skipZero) {2let sum = 0;3let count = 0;4for (const value of values) {5if (skipZero && value === 0) continue;6sum += value;7count += 1;8}9return sum / count;10}색 막대: 테스트가 실행한 줄(실행 가능한 줄 전부)테스트(모두 통과)average([1,2,3], false) = 2average([0,4], true) = 4줄 커버리지 100%분기 커버리지 100%그래도 남은 버그average([0], true) = NaNaverage([], false) = NaN
테스트 두 개로 줄·분기 커버리지 100%를 채운 평균 함수. 둘 다 통과한다. 그런데 0만 들어오거나 빈 배열이 들어오면 count가 0이라 0 ÷ 0 = NaN이 된다. 빠진 것은 줄이 아니라 ‘값이 하나도 남지 않는’ 입력의 경우다.
  • 단언 없는 테스트: 함수를 부르기만 하고 결과를 확인하지 않아도 커버리지는 올라간다. 숫자를 목표로 삼으면 이런 테스트가 늘어난다.
  • 쓸모 있게 보기: 전체 숫자보다 바뀐 코드의 커버리지를 PR마다 보는 것이 낫다. 구글의 지침은 60%를 ‘괜찮음’, 75%를 ‘칭찬할 만함’, 90%를 ‘모범’으로 두면서도, 90%에서 95%로 올리는 데 매달리지 말라고 한다. 커버리지 보고서의 진짜 쓸모는 ‘아무도 시험하지 않는 갈래’를 찾아 주는 것이다.

속성 기반 테스트

예제 기반 테스트는 사람이 고른 입력 몇 개로 시험한다. 위의 평균 함수처럼 사람이 떠올리지 못한 입력에서 버그가 나온다. 속성 기반 테스트(property-based testing)는 입력 대신 모든 입력에서 성립해야 할 성질을 적고, 도구가 무작위 입력 수백 개를 만들어 시험한다. 2000년 Haskell의 QuickCheck에서 시작해 Python의 Hypothesis, JavaScript의 fast-check 등으로 퍼졌다.

줄이기 단계 그림: 입력 배열과 기본 sort() 결과를 보여 주고, 오름차순이 깨진 자리를 표시한다. 단계마다 원소를 빼거나 수를 줄여 마지막에 [10, 2]가 남는다속성: 모든 입력 xs에 대해 xs.sort()는 오름차순이다입력sort() 결과37510281023758원소 4개합 152✗ 속성 깨짐5102810258원소 3개합 115✗ 속성 깨짐10281028원소 2개합 110✗ 속성 깨짐518518원소 2개합 59✗ 속성 깨짐258258원소 2개합 33✗ 속성 깨짐128128원소 2개합 20✗ 속성 깨짐118118원소 2개합 19✗ 속성 깨짐108108원소 2개합 18✗ 속성 깨짐104104원소 2개합 14✗ 속성 깨짐102102원소 2개합 12✗ 속성 깨짐102102원소 2개합 12✗ 속성 깨짐줄이기 단계빨간 테두리: 앞 값이 뒤 값보다 커서 오름차순이 깨진 자리
1 / 11

무작위 입력 [37, 5, 102, 8]에서 속성이 깨졌다. 이제 줄인다.

속성 기반 테스트의 줄이기. 도구는 ‘정렬 결과는 오름차순이다’라는 속성을 무작위 입력 수백 개로 시험하다가 깨지는 입력을 찾으면, 여전히 실패하는 더 작은 입력을 찾아 줄여 간다. 기본 sort()가 숫자를 글자로 비교한다는 버그가 [10, 2]에서 한눈에 드러난다.

무작위로 찾은 반례는 대개 크고 지저분하다. 그래서 도구는 반례를 찾으면 줄이기(shrinking)를 한다. 원소를 빼거나 수를 작게 바꾼 후보 중 여전히 실패하는 것을 골라 더 줄일 수 없을 때까지 되풀이한다. 위 예에서 10단계 만에 [10, 2]가 남았고, 이것을 정렬하면 [10, 2]이다. JavaScript의 기본 sort()가 숫자를 글자로 바꿔 ‘10’ < ‘2’로 비교한다는 원인이 곧바로 보인다.

  • 자주 쓰는 성질: 되돌리기(디코드(인코드(x)) = x), 불변량(정렬해도 원소 수와 구성은 같다), 두 구현 비교(빠른 새 구현 = 느리지만 확실한 옛 구현), 멱등(두 번 해도 한 번 한 것과 같다).
  • 재현: 무작위라도 시드를 기록해 같은 입력을 다시 만들 수 있고, 한 번 찾은 반례는 예제 테스트로 고정해 둔다.

뮤테이션 테스트: 테스트를 시험하기

커버리지가 ‘실행했는가’라면 뮤테이션 테스트는 ‘틀렸을 때 알아채는가’를 잰다. 코드에 작은 고장을 일부러 넣은 판(뮤턴트, 예: >= → >)을 많이 만들고 테스트를 돌린다. 테스트가 실패하면 뮤턴트를 죽인 것이고, 통과하면 뮤턴트가 살아남은 것이다. 죽인 비율이 뮤테이션 점수다.

뮤테이션 테스트 표: 뮤턴트 여덟 개 중 입력 30과 10만으로는 다섯 개를 죽여 62.5%, 경계값 18을 더하면 87.5%, 17까지 더하면 100%가 된다원래 식: age >= 18뮤턴트 \ 입력30101817age > 18··죽임·age < 18죽임죽임죽임죽임age <= 18죽임죽임·죽임age == 18죽임···age >= 17···죽임age >= 19··죽임·true·죽임·죽임false죽임·죽임·테스트 입력 → 뮤테이션 점수{30, 10}62.5%+ 1887.5%+ 17100%살아남은 뮤턴트는 ‘이 차이를 아무 테스트도 알아채지 못한다’는 뜻이다. 셋 다 경계값 문제였다.
성인 판정 함수의 뮤턴트 여덟 개. 입력 30과 10은 ‘확실한 어른과 확실한 아이’라 경계 근처를 바꾼 뮤턴트를 하나도 잡지 못한다. 경계값을 더할 때마다 점수가 오른다.

입력 30·10만 있는 테스트는 줄 커버리지 100%이지만 뮤테이션 점수는 62.5%다. 살아남은 age > 18, age >= 17, age >= 19는 모두 18 언저리를 바꾼 것이다. 18을 더하면 87.5%, 17까지 더하면 100%가 된다. 뮤테이션 테스트가 가리키는 것은 정확히 사람이 놓치기 쉬운 경계값이다.

  • 도구: Java의 PIT, JavaScript·C#·Scala의 Stryker가 대표적이다.
  • 비용: 뮤턴트마다 테스트를 다시 돌리니 무겁다. 바뀐 코드에만 돌리거나, 구글처럼 의미 없는 줄(로그 등)을 빼고 고른 뮤턴트를 코드 리뷰에 보여 주는 방식으로 줄인다(State of Mutation Testing at Google).
  • 같은 뜻의 뮤턴트: 바꿔도 동작이 같은 뮤턴트(equivalent mutant)는 어떤 테스트로도 죽일 수 없다. 그래서 100%를 목표로 삼기보다 살아남은 뮤턴트를 하나씩 읽어 보는 데 쓴다.

불안정한 테스트

코드를 바꾸지 않았는데 통과와 실패를 오가는 테스트가 불안정한 테스트(flaky test)다. 구글은 전체 테스트 실행의 약 1.5%가 이런 결과를 내고, 통과에서 실패로 바뀐 사례의 84%에 불안정한 테스트가 끼어 있었다고 보고했다. 하나하나는 드물어도 개수가 쌓이면 확률이 곱해진다.

불안정한 테스트 그래프: 테스트마다 0.1% 확률로 흔들릴 때 테스트 수가 500개면 묶음이 빨간불일 확률이 약 39%, 1000개면 63%, 2000개면 86%이고, 재시도를 한 번 하면 거의 0에 붙는다0%25%50%75%100%0500100015002000테스트 수(각각 0.1% 확률로 흔들림)39%63%86%재시도 1번: 거의 0%빨간 선: 묶음이 한 번이라도 빨간불일 확률
테스트마다 0.1% 확률로 이유 없이 실패할 때, 묶음 전체에 빨간불이 하나라도 켜질 확률. 실패한 테스트를 한 번 더 돌리면 거의 0이 되지만, 원인은 그대로 남는다.

0.1%로 흔들리는 테스트가 500개면 묶음이 빨간불일 확률이 39%, 1,000개면 63%다. 이 정도면 개발자는 빨간불을 ‘또 그거겠지’ 하고 무시하기 시작하고, 진짜 실패도 함께 묻힌다. 자동 재시도 한 번이면 1,000개에서도 0.10%로 떨어지지만, 재시도는 증상을 덮을 뿐이다.

  • 흔한 원인: 고정 시간 대기(sleep(1000)), 테스트끼리 공유하는 상태와 실행 순서 의존, 현재 시각·시간대·무작위 값, 네트워크와 외부 서비스, 동시성 경쟁.
  • 다루는 법: 재시도해서 통과한 테스트를 기록해 불안정 목록을 만들고, 격리(quarantine)해 배포를 막지 않게 한 뒤 담당자를 정해 고친다. 고칠 때는 조건을 기다리는 대기, 테스트마다 새 데이터, 시계와 난수의 주입으로 비결정성을 걷어 낸다(Eradicating Non-Determinism in Tests).

TDD: 테스트를 먼저

테스트 주도 개발(TDD)은 켄트 벡(Kent Beck)이 정리한 짧은 순환이다. 실패하는 테스트를 하나 쓰고(빨강), 그것을 통과할 만큼만 코드를 쓰고(초록), 테스트가 초록인 채로 구조를 다듬는다(리팩터링). 이 순환을 몇 분 단위로 돈다.

TDD 순환: 실패하는 테스트를 먼저 쓰고(빨강), 통과할 만큼만 구현하고(초록), 테스트를 초록으로 둔 채 구조를 다듬은(리팩터링) 뒤 다시 처음으로 돌아간다빨강실패하는 테스트초록통과할 만큼만리팩터링초록 유지한 채최소 구현중복 제거·이름 다듬기다음 테스트
빨강-초록-리팩터링. 테스트가 먼저 실패하는 것을 봐야 그 테스트가 정말 무언가를 검사한다는 것이 확인된다.

얻는 것은 테스트 자체보다 설계 압력이다. 테스트부터 쓰려면 함수를 밖에서 부르기 쉬운 모양으로 생각해야 하니, 의존성이 드러나고 작게 나뉜 코드가 나오기 쉽다. 반대로 무엇을 만들지 아직 모르는 탐색 단계나 UI 배치처럼 결과를 눈으로 봐야 하는 일에는 맞지 않는다. 그런 때는 먼저 만들어 보고(스파이크) 방향이 정해지면 테스트와 함께 다시 쓴다.

  • 오해: 테스트를 전부 먼저 쓴다: TDD는 시험 목록을 먼저 적어 두되, 그중 하나만 골라 빨강-초록-리팩터링을 돌고 다음으로 넘어간다. 테스트 수십 개를 한꺼번에 써 두는 방식이 아니다.
  • 오해: 초록이면 끝: 마틴 파울러는 TDD를 망치는 가장 흔한 길로 세 번째 단계인 리팩터링을 빼먹는 것을 꼽는다. 통과만 시키고 다듬지 않으면 코드 조각을 덕지덕지 붙인 결과가 남는다(TestDrivenDevelopment).

계약 테스트

서비스가 여럿으로 나뉘면 ‘주문 서비스가 기대하는 응답’과 ‘상품 서비스가 실제로 주는 응답’이 어긋나는 사고가 잦다. 둘을 함께 띄우는 E2E로 잡으려면 느리고 불안정하다. 소비자 주도 계약 테스트는 이 사이의 약속만 따로 시험한다. 서비스 사이 계약(엄격·느슨한 계약)의 설계는 시스템 설계 › 분해와 분산 데이터에서 다뤘다.

소비자 주도 계약 테스트: 소비자 테스트가 가짜 제공자와 대화하며 기대를 계약 파일로 남기고, 브로커가 이를 보관하며, 제공자는 진짜 서비스에 계약의 요청을 재생해 응답이 맞는지 확인한다. 배포 전에 두 판이 서로 맞는지 브로커에 묻는다소비자 테스트가짜 제공자에 대고요청·기대 응답 기록계약 파일GET /orders/7→ 200 {id, total}브로커계약과 검증 결과판별로 보관제공자 검증진짜 제공자에계약 요청을 재생소비자 팀 CI제공자 팀 CI검증 결과can-i-deploy? → 예/아니오배포 전에 묻는다:
Pact 방식의 흐름. 두 팀은 서로의 서비스를 띄우지 않고 각자의 CI에서 계약만 시험한다. 소비자가 쓰지 않는 필드는 제공자가 마음대로 바꿔도 된다는 것이 계약으로 드러난다.
  • 소비자 쪽: 가짜 제공자(목 서버)를 상대로 테스트를 돌리며 ‘이 요청에 이런 모양의 응답을 기대한다’를 계약 파일로 남긴다. 실제로 쓰는 필드만 적힌다.
  • 제공자 쪽: 진짜 제공자 서비스에 계약의 요청을 재생해 응답이 기대와 맞는지 본다. 필드를 지우거나 이름을 바꾸면 그 필드를 쓰는 소비자가 있을 때만 실패한다.
  • 배포 전 질문: Pact 브로커는 ‘운영에 있는 소비자 판들과 이 제공자 판이 모두 검증됐나’에 답한다(can-i-deploy). 아니면 배포를 멈춘다.

핵심 자료

자료한 줄 요지
The Practical Test Pyramid (Ham Vocke)피라미드의 층마다 무엇을 어떻게 시험하는지 예제 코드와 함께 정리한 긴 글.
The Testing Trophy (Kent C. Dodds)정적 분석을 바닥에 깔고 통합 테스트를 가장 두껍게 두자는 트로피 모델을 제안한 글.
Mocks Aren't Stubs (Martin Fowler)상태 검증과 행위 검증, 고전파와 목 중심파의 차이를 테스트 더블 용어로 설명한다.
Code Coverage Best Practices (Google Testing Blog)커버리지 숫자의 쓸모와 한계, 변경분 커버리지를 보라는 실무 지침.
HypothesisPython의 대표 속성 기반 테스트 라이브러리. 입력 생성과 줄이기를 자동으로 한다.
State of Mutation Testing at Google (Petrović, Ivanković)모든 변경에 뮤턴트를 다 돌리는 대신 코드 리뷰에 흥미로운 뮤턴트만 보여 주는 방식을 6,000명 규모로 운영한 경험.
Flaky Tests at Google and How We Mitigate Them테스트 실행의 약 1.5%가 흔들리고, 통과→실패 전환의 84%가 흔들리는 테스트였다는 측정.
Pact 문서소비자 주도 계약 테스트 도구. 계약 파일, 브로커, can-i-deploy의 흐름을 설명한다.