Skip to content

ADR-02: 동적 테스트 카운팅 정책

🇺🇸 English Version

날짜작성자영향 리포지토리
2025-12-22@KubrickCodecore

상태: 승인됨 구현: ✅ Phase 1 완료 (2025-12-22)

Context

SpecVital Core 파서는 정적 AST 분석 기반 테스트 카운트 수행. 많은 테스트 프레임워크가 런타임 실행 없이는 정확한 카운트 불가능한 동적 테스트 생성 패턴 지원.

발견 경위

github-project-status-viewer 대상 검증 결과:

  • 실제값 (CLI): 236 테스트
  • 파서 결과: 229 테스트
  • 차이: -7 (2.97%)

근본 원인: 동적 테스트 패턴 미지원.

Decision

정책: 동적 테스트를 1로 카운트

모든 동적 생성 테스트 패턴은 실제 런타임 카운트와 관계없이 1개 테스트로 카운트.

근거

  1. 정적 분석의 한계: 런타임 값 평가 불가
  2. 일관성: 20개 프레임워크 전체에서 동일한 동작
  3. 복잡도 vs 가치: 배열 리터럴 파싱은 한계 이익만 제공
  4. 탐지 우선순위: 테스트 존재 탐지 > 정확한 카운트

Options Considered

Option A: 동적 테스트를 1로 카운트 (선택됨)

모든 동적 패턴을 단일 테스트로 균일하게 처리.

장점:

  • 프레임워크 간 일관된 동작
  • 단순한 구현
  • 정확도에 대한 거짓 약속 없음
  • 한계에 대한 명확한 문서화

단점:

  • 파서 카운트와 CLI 카운트 차이 가능
  • 정확한 카운트를 위한 CLI 필요

Option B: 배열 리터럴 파싱

it.each([1,2,3]) 같은 정적 패턴에서 배열 요소 카운트 시도.

장점:

  • 단순한 케이스에서 더 정확

단점:

  • 비일관적 (리터럴은 됨, 변수는 안됨)
  • 복잡한 구현
  • 미미한 정확도 향상

Option C: 런타임 실행 필요

테스트를 실행하여 정확한 카운트 획득.

장점:

  • 100% 정확도

단점:

  • 코어의 정적 분석 접근 방식의 근본적 변경
  • 테스트 환경 설정 필요
  • 느린 실행
  • 보안 우려

프레임워크 분석

프레임워크별 동적 테스트 패턴

프레임워크동적 패턴현재 지원정책
JavaScript/TypeScript
Jestit.each([...])부분1 + (dynamic cases)
JestforEach + it1 + (dynamic cases)
Vitestit.each([...])부분1 + (dynamic cases)
VitestforEach + it1 + (dynamic cases)
MochaforEach + it1 + (dynamic cases)
CypressforEach + it1 + (dynamic cases)
Playwrightloop + test1
Python
pytest@pytest.mark.parametrize1
unittestsubTest1
Java
JUnit5@ParameterizedTest1
JUnit5@RepeatedTest1
TestNG@DataProvider1
Kotlin
KotestforAll, data-driven1
C#
NUnit[TestCase] 복수N (attribute 카운트)
NUnit[TestCaseSource]1
xUnit[Theory] + [InlineData]N (attribute 카운트)
xUnit[MemberData]1
MSTest[DataRow] 복수N (attribute 카운트)
MSTest[DynamicData]1
Ruby
RSpecshared_examples1
Minitestloop + def test_1
Go
go-testingt.Run in loopN (감지된 subtest)
go-testingtable-driven (변수)부분감지된 row만
Rust
cargo-test#[test_case]1
C++
GoogleTestINSTANTIATE_TEST_SUITE_P1
Swift
XCTest네이티브 parametrized 없음N/A-
PHP
PHPUnit@dataProvider1

범례

  • ✅ 지원: 실제 케이스 카운트
  • 부분: 패턴 감지하지만 모든 케이스 카운트 못함
  • ❌ 미지원: 1로 카운트
  • ❌ 버그: 감지해야 하지만 현재 안됨

Linter 테스트 유틸리티

린터 테스트 유틸리티(ESLint RuleTester, Stylelint 등)는 표준 테스트 프레임워크 API(it, test)를 호출하지 않고 내부적으로 테스트를 생성함. 동적 테스트로 처리.

유틸리티패턴정책
ESLint RuleTesterruleTester.run('rule', rule, { valid, invalid }).run() 호출당 1개
StylelintstylelintTester.run('rule', rule, { accept, reject }).run() 호출당 1개

감지 기준:

  • 호출자 변수명에 "tester" 포함 (대소문자 무관)
  • 메서드명이 run
  • 첫 번째 인자가 문자열 리터럴 (규칙 이름)
  • 최소 3개 인자

Consequences

Positive

  • 프레임워크 간 일관된 동작
  • 단순한 구현
  • 정확도에 대한 거짓 약속 없음
  • 한계에 대한 명확한 문서화

Negative

  • 파서 카운트와 CLI 카운트 차이 가능
  • 정확한 카운트를 위한 CLI 필요

Neutral

  • 실제값 검증 시 동적 테스트 고려 필요

Implementation

Phase 1: 버그 수정 (완료 ✅)

테스트 감지 필요하나 현재 0 반환하는 패턴 수정:

  1. JS/TS: forEach/map 콜백 내 it/test수정 완료 (2025-12-22)
  2. JS/TS: 객체 배열이 있는 it.each([{...}]) (현재 0, 1이어야 함)수정 완료 (2025-12-22)

Phase 2: 개선 (선택)

카운트가 정적으로 결정 가능한 attribute 기반 parametrized 테스트 카운트 고려:

  • C#의 [TestCase(...)] × N
  • 리터럴 배열이 있는 @pytest.mark.parametrize("x", [1,2,3])

Phase 3: Linter 테스트 유틸리티 (완료 ✅)

표준 테스트 API를 우회하는 린터 테스트 유틸리티 지원:

  1. JS/TS: ruleTester.run() ESLint 패턴수정 완료 (2025-12-29)
  2. JS/TS: stylelintTester.run() Stylelint 패턴수정 완료 (2025-12-29)

Open-source test coverage insights