Framework Testing Guide
이 문서는 _Framework 의 테스트를 작성/실행하는 표준 방법을 설명한다.
작업 전 MCP_README.md -> MCP_Rules.md -> MCP_Index.md 순서로 먼저 확인할 것.
1. Assembly 구조
Assets/_Framework/Tests/
Shared/ Framework.Tests.Shared.asmdef (Any Platform)
TestFramework/ 공통 Fixture 기반 클래스
Utilities/ 공통 Mock/Helper
EditMode/ Framework.Tests.EditMode.asmdef (Editor 전용)
Runtime/Common/
Runtime/Core/
Runtime/Patterns/
Runtime/Systems/
Integration/
Performance/
Regression/
PlayMode/ Framework.Tests.PlayMode.asmdef (Any Platform)
Runtime/Systems/ 실시간 대기([UnityTest])가 필요한 테스트만
- 실제 시간 경과(
WaitForSeconds, 실시간 지연)나 프레임 진행이 필요한 테스트만 PlayMode 에 둔다. - 그 외 모든 테스트는 EditMode 에 둔다(실행 속도가 훨씬 빠르다).
- 새 테스트 파일을 추가할 때 반드시 EditMode/PlayMode 중 맞는 폴더에 넣을 것 — 잘못된 폴더에 두면 Unity Test Runner 가 해당 모드에서 테스트를 인식하지 못한다.
알려진 제약 (Runtime 문제, 미수정)
PlayMode 진입 시 RootBootstrap.Initialize()(회피 불가)가 RootScope.cs 의 DI 등록 누락
(ConfigCache 미등록)으로 실패해 TickRunner.Update() 가 매 프레임 NullReferenceException 을
로그로 남긴다. 이로 인해 현재 모든 PlayMode 테스트는 SetUp 단계에서 실패한다.
근본 해결은 RootScope.cs 에 ConfigCache DI 등록을 추가하는 것이며, Runtime 코드 변경이 필요하다.
2. AAA Pattern
모든 테스트 메서드는 Arrange / Act / Assert 3 구획을 주석으로 명시한다.
[Test]
public void SetBaseValue_NewStat_ReturnsGivenValue()
{
// Arrange
var stat = new StatSystem();
var id = new StatId("Health");
// Act
stat.SetBaseValue(EntityId, id, 10f);
// Assert
Assert.AreEqual(10f, stat.GetValue(EntityId, id));
}
Regression Test 는 AAA 대신 Given/When/Then 네이밍을 메서드명에 사용한다(4번 참고).
3. Fixture 사용법
| 기반 클래스 | 위치 | 용도 |
|---|---|---|
FrameworkTestBase |
Shared | 최상위 추상 기반(공용 로직 없음, 명명 규칙 문서화 목적) |
FrameworkEditModeTestBase |
Shared | Scene/GameObject 불필요한 순수 로직, new 로 생성 가능한 System |
FrameworkPlayModeTestBase |
Shared | 실시간 대기/프레임 진행이 필요한 테스트 |
SystemTestFixture |
Shared | FrameworkEditModeTestBase 를 상속하는 System 테스트 전용 별칭 |
RegressionTestBase |
EditMode/Regression | Regression Test 전용(SystemTestFixture 상속) |
SetUp/TearDown 을 override 할 때는 반드시 base.SetUp()/base.TearDown() 을 호출한다.
4. System Test 작성법
- 테스트 대상 System 을
new로 직접 생성한다(Framework System 은 생성자 DI 이므로 Reflection/컨테이너 불필요). - 실제 Framework 구현체(
NewtonsoftJsonAdapter,MemoryPackAdapter등)를 우선 사용한다. Mock 은 실제 구현을 쓰기 어려운 경우(예:IMessageBus)에만 최소한으로 사용한다. - System 이 정상 설계상
Log.Error/Log.Exception을 남기는 경로를 테스트할 때는 반드시UnityEngine.TestTools.LogAssert.Expect(...)로 그 로그를 명시적으로 기대한다. 그렇지 않으면 Unity Test Runner 가 예상치 못한 로그로 간주해 테스트를 자동 실패 처리한다. DiagnosticsCounters는 Pull 기반(0.5초 SampleInterval)이다. Register 직후 값을 확인하려면DiagnosticsCounters.Tick(1f)(SampleInterval 초과 deltaTime)을 먼저 호출해 강제 샘플링한다.Stat의OnValueChanged계열 이벤트는 Lazy 재계산 방식이라,SetBaseValue직후 곧바로 이벤트가 발생하지 않는다.GetValue(FinalValue 조회)를 한 번 호출해 재계산을 트리거해야 한다.- Addressables/AudioMixer/Locale 등 프로젝트에 실제 리소스·설정이 구성되지 않은 영역은 성공 경로를
검증할 수 없다. 이 경우 실패 경로(유효성 검사, 미존재 키 처리)만 다루고 XML
<remarks>에 범위 한계와 사유를 명시한다.
5. Integration Test 작성법
Assets/_Framework/Tests/EditMode/Integration/에 둔다.- 두 System 을 각각
new로 생성하고, Framework 가 제공하지 않는 결합(예: Effect -> Stat)은 테스트 메서드 내부에서 이벤트 구독으로 직접 오케스트레이션한다(신규 Runtime 결합 코드 작성 금지). - Reflection 사용 금지, Runtime 코드 수정 금지.
6. Regression Test 작성법
Assets/_Framework/Tests/EditMode/Regression/에RegressionTestBase를 상속해 작성한다.- 클래스명:
Regression_<System>_<이슈요약>Tests - 메서드명:
Given_<사전조건>_When_<행동>_Then_<기대결과>형태. - XML
<remarks>에 발생 시점/증상/원인/수정 내용을 반드시 기록한다. - 예제:
Regression_DiagnosticsCounters_GetLatestRequiresTickTests.cs
7. Performance Test 작성법
Assets/_Framework/Tests/EditMode/Performance/에 둔다.System.Diagnostics.Stopwatch로 측정하고, Pass/Fail 판정 없이UnityEngine.Debug.Log로 참고용 수치만 출력한다(기존PerformanceBenchmarks.cs패턴을 따른다).- 실제 파일 I/O 등 비용이 큰 작업은 반복 횟수를 낮춘다(예: SaveSystem 200회).
8. Test Report 확인
Tools > Framework > Test Report 메뉴에서:
- EditMode/PlayMode 각각 실행 및 최근 결과 확인
- System별 테스트 수/PASS/FAIL 확인(테스트 개수 기반 근사치)
TestResults.xml바로 열기
정밀 라인/브랜치 커버리지가 필요하면 Unity Test Coverage 패키지(설치됨)를 배치모드로 실행한다:
Unity.exe -projectPath <path> -batchmode -testPlatform editmode -runTests ^
-debugCodeOptimization -enableCodeCoverage ^
-coverageResultsPath <path> -coverageOptions generateHtmlReport;generateBadgeReport -quit
9. 공통 원칙
- Reflection 사용 금지 (Framework.Tests.* 어셈블리에는
InternalsVisibleTo가 허용되어 있어 internal 멤버는 직접 접근 가능하다 — Reflection 이 필요 없다) - Thread.Sleep 금지 — PlayMode 는
yield return new WaitForSeconds(...), EditMode 는 실시간 대기를 피하고 필요 시 internal API(Tick 등)로 즉시 트리거한다. - Mock 최소화 — 실제 Framework 구현을 우선 사용하고, 미사용 Mock 은 주기적으로 점검해 제거한다.