Infection으로 변이 테스트
테스트가 실제로 얼마나 우수한지 측정합니다.
Infection으로 변이 테스트은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
커버리지는 거짓말을 합니다
줄 커버리지 100%는 안심이 되지만, 테스트가 코드를 실행했다는 것만 증명할 뿐 코드의 버그를 발견할 수 있다는 것은 증명하지 못합니다. 테스트가 한 줄을 실행하고도 의미 있는 검증을 전혀 하지 않을 수 있습니다. 변이 테스트는 실제로 중요한 것을 측정합니다. 코드가 미묘하게 망가졌을 때 테스트가 실패할까요? Infection은 이를 위한 표준 PHP 도구입니다.
composer require --dev infection/infection핵심 개념: 변이체
Infection은 커버된 코드에 작은 결함인 변이체를 주입합니다. >를 >=로, +를 -로, &&를 ||로 바꾸거나 return을 제거하는 식입니다. 그런 다음 각 변이체를 대상으로 테스트를 다시 실행합니다.
- 테스트가 실패하면 → 변이체가 제거됩니다(좋습니다. 테스트가 결함을 발견했다는 뜻입니다).
- 모든 테스트가 통과하면 → 변이체가 살아남습니다(나쁩니다. 여기에 실제 버그가 있어도 발견되지 않는다는 뜻입니다).
살아남은 변이체가 실제로 나타나는 경우
다음 함수와 취약한 테스트를 생각해 보십시오. Infection은 >=를 >로 바꿀 것입니다. 금액이 임계값과 같은 정확한 경계를 확인하는 테스트가 없다면 해당 변이체가 살아남아 테스트되지 않은 경계를 드러냅니다.
<?php
function qualifiesForFreeShipping(float $total): bool
{
return $total >= 50.0; // Infection mutates >= to >
}
// Weak test only checks 100 and 10 -> never tests exactly 50.0
var_dump(qualifiesForFreeShipping(100.0)); // true
var_dump(qualifiesForFreeShipping(10.0)); // false
var_dump(qualifiesForFreeShipping(50.0)); // true <-- the boundary the mutant exposes
변이체 제거하기
경계 검증을 추가하면 >=에서 >로 바뀐 변이체가 제거됩니다. 변이된 코드에서는 50.0 > 50.0이 false이므로 테스트가 실패하며, 이것이 바로 원하는 결과입니다. 변이 테스트는 어떤 검증이 빠졌는지 문자 그대로 알려줍니다.
<?php
use PHPUnit\Framework\TestCase;
final class ShippingTest extends TestCase
{
public function test_threshold_is_inclusive(): void
{
// Kills the >= -> > mutant
self::assertTrue(qualifiesForFreeShipping(50.0));
}
}
설정: infection.json
Infection은 infection.json5를 기반으로 실행됩니다. 변이시킬 디렉터리, 로그를 저장할 위치, CI 통과에 필요한 최소 점수 임계값을 선언합니다. source.directories는 운영 코드만 가리키도록 하십시오. 테스트 코드는 절대 포함하지 마십시오.
{
"source": {
"directories": ["src"]
},
"logs": {
"text": "build/infection.log",
"html": "build/infection.html"
},
"mutators": {
"@default": true
},
"minMsi": 80,
"minCoveredMsi": 90
}MSI 지표
Infection은 변이 점수 지표를 보고합니다.
- MSI = 제거된 변이체 수 / 전체 변이체 수. 커버되지 않은 코드의 영향을 받아 점수가 낮아집니다(해당 변이체는 제거할 수 없기 때문입니다).
- 커버된 MSI = 제거된 변이체 수 / 커버된 줄에 있는 변이체 수. 실제로 테스트하는 부분에서 검증이 얼마나 강력한지 측정합니다.
- 변이 코드 커버리지 = Infection이 변이시킬 수 있었던 코드의 비율입니다.
줄 커버리지는 높지만 커버된 MSI가 낮다면 검증이 부족한 테스트라는 전형적인 신호입니다.
Infection을 효율적으로 실행하기
변이 테스트는 변이체 하나마다 테스트 모음을 한 번씩 다시 실행하므로 비용이 많이 듭니다. 속도를 크게 높이는 두 가지 방법은 --threads로 테스트를 병렬 실행하는 것과, Git 차이 필터를 사용해 현재 브랜치에서 변경된 코드만 변이시키는 것입니다. 후자는 풀 리퀘스트의 CI에 특히 적합합니다.
vendor/bin/infection --threads=max --git-diff-lines --git-diff-base=origin/main살아남은 변이체 읽기
각각의 살아남은 변이체에 대해 Infection이 출력하는 차이에 핵심 정보가 있습니다. 테스트가 감지하지 못한 변경과 정확한 줄을 보여 줍니다. 살아남은 변이체를 할 일 목록으로 여기십시오. 누락된 검증을 추가하거나, 해당 변이체가 무해한 동등한 변이체임을 확인하면 됩니다.
- return $total >= 50.0;
+ return $total > 50.0;
# Mutant survived: no test asserts the inclusive boundary (total === 50.0)동등한 변이체와 무시하기
일부 변이체는 동등합니다. 코드는 변경하지만 관찰 가능한 동작은 바꾸지 않으므로 어떤 테스트도 제거할 수 없습니다. 이런 변이체는 제거할 수 없고 MSI를 부당하게 낮춥니다. 테스트를 조작해 동등한 변이체를 쫓기보다는 특정 코드에서 알려진 오탐 변이 연산자를 억제하십시오.
{
"mutators": {
"@default": true,
"Plus": {
"ignore": ["App\\Math\\Statistics::variance"]
}
}
}변이 테스트의 효과가 큰 곳
변이 테스트는 느리므로 대상을 잘 선택하십시오.
- 핵심 도메인 로직에 적용하십시오. 가격 책정, 권한, 계산처럼 조용한 오류의 비용이 큰 부분이 대상입니다.
- 전체 저장소가 아니라 변경된 줄의 커버된 MSI를 기준으로 풀 리퀘스트 통과 여부를 검사하십시오.
- 전체적으로 100%를 달성하려고 하지 마십시오. 수확 체감과 동등한 변이체 때문에 비효율적입니다.
- 변이 테스트를 취약한 검증을 찾는 데 사용한 다음 테스트를 수정하십시오. 점수는 수단이지 목표가 아닙니다.
빠르게 실행하려면 커버리지 데이터가 필요합니다
Infection은 테스트가 실제로 커버한 줄만 변이시키므로 테스트 실행기의 커버리지를 재사용합니다. Xdebug를 사용하면 느리지만, pcov는 줄 커버리지를 훨씬 빠르게 수집하므로 변이 테스트 실행에 권장되는 드라이버입니다. Infection이 직접 커버리지를 생성하게 할 수도 있고, CI에서 이미 생성한 커버리지 보고서를 사용할 수도 있습니다.
# Faster mutation runs: use pcov instead of Xdebug for coverage
php -d pcov.enabled=1 vendor/bin/infection --threads=max
# Or reuse coverage already generated by your PHPUnit step:
vendor/bin/infection --coverage=build/coverage --skip-initial-tests빠른 확인
살아남은 변이체는 무엇을 알려 줄까요?
복습
테스트의 양이 아니라 품질을 측정하는 방법을 배웠습니다.
- 변이 테스트는 작은 결함(변이체)을 주입합니다. 제거됨 = 발견됨, 살아남음 = 빈틈이 있음입니다.
- 커버리지는 실행 여부를 보여 주고, 커버된 MSI는 검증의 강도를 보여 줍니다.
infection.json5에서 MSI 임계값을 설정하고, 속도를 높이려면--threads와 Git 차이 필터링을 사용합니다.- 살아남은 변이체는 할 일 목록입니다. 동등한 변이체를 확인하고 의도적으로 무시하십시오.
- 핵심 로직을 대상으로 하고, 전체 100%를 추구하기보다 변경된 줄의 MSI를 기준으로 풀 리퀘스트를 검사하십시오.
자주 묻는 질문
“Infection으로 변이 테스트” 강의는 무료인가요?
네 — “Infection으로 변이 테스트” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“Infection으로 변이 테스트”에서 뭘 배우나요?
테스트가 실제로 얼마나 우수한지 측정합니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
PHP Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“Infection으로 변이 테스트” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 테스트 주도 개발 워크플로
- Mockery로 모킹과 스터빙
- 통합 테스트와 기능 테스트
- Infection으로 변이 테스트