Quick Reference
- 기준은 Unity 6.5 (6000.5), Unity Test Framework 1.6.0입니다. 규칙·계산·직렬화는 빠른 EditMode, scene·MonoBehaviour lifecycle·coroutine·실제 input/physics 연결은 필요한 경우만 PlayMode로 나눕니다.
- test code는 production Runtime asmdef와 별도 test asmdef에 두고 Test Assemblies를 켭니다. test asmdef는 Runtime을 참조하지만 Runtime은 test를 참조하지 않습니다.
- CI는
-runTests -batchmode -projectPath -testPlatform -testResults를 기본 명령으로 두고 XML 결과와 Editor log를 artifact로 남깁니다. test 종료 전에-quit을 붙이지 않습니다.
text
Game.Runtime.asmdef
Game.Tests.EditMode.asmdef -> Game.Runtime, Test Assemblies, Editor platform
Game.Tests.PlayMode.asmdef -> Game.Runtime, Test AssembliesTest Assembly와 Runner 설정
- Project 창에서 test 폴더에 Assembly Definition을 만들고 Inspector의 Test Assemblies를 켭니다. 이 설정이
nunit.framework와 Test Framework runner reference를 추가합니다. 체크하지 않으면[Test],Assert를 찾지 못합니다. - EditMode test asmdef는 Include Platforms에서
Editor를 선택합니다. Editor API와 빠른 순수 규칙 test에 맞습니다. Runtime asmdef가UnityEditor를 참조하지 않게 분리해야 player build에도 test dependency가 새지 않습니다. - PlayMode test는 Test Runner의 PlayMode tab에서 실제 player loop로 실행됩니다. 씬, prefab,
Awake/Start, coroutine, physics가 핵심일 때만 사용합니다. 같은 규칙을 EditMode와 PlayMode에 중복해 두기보다, EditMode는 결과 규칙, PlayMode는 engine 연결을 검증합니다. - Test Runner는 EditMode, PlayMode, target platform Player에서 실행할 수 있습니다. Editor PlayMode가 통과해도 target device의 graphics/input/build setting 문제가 없다는 보장은 아니므로 release 전에 필요한 smoke test는 실제 Player에서도 따로 둡니다.
- Test Framework는 Unity Editor에 포함되는 core package이며 NUnit 기반 API와 Unity-specific
UnityTest를 제공합니다. package의 NUnit 버전과 일반 .NET NUnit의 최신 API를 같다고 가정하지 말고, 지원 API는 Unity 기준 문서를 확인합니다.
NUnit과 UnityTest
csharp
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public sealed class HealthTests
{
[Test]
public void TakeDamage_ClampsAtZero()
{
var health = new HealthSystem(10);
health.TakeDamage(20);
Assert.That(health.Current, Is.EqualTo(0));
}
[UnityTest]
public IEnumerator DisabledObject_StopsItsUpdate()
{
var gameObject = new GameObject("Probe");
var probe = gameObject.AddComponent<UpdateProbe>();
yield return null;
int countBeforeDisable = probe.Count;
gameObject.SetActive(false);
yield return null;
Assert.That(probe.Count, Is.EqualTo(countBeforeDisable));
Object.Destroy(gameObject);
}
}
internal sealed class HealthSystem
{
public int Current { get; private set; }
public HealthSystem(int current)
{
Current = current;
}
public void TakeDamage(int amount)
{
Current = Mathf.Max(0, Current - amount);
}
}
public sealed class UpdateProbe : MonoBehaviour
{
public int Count { get; private set; }
private void Update()
{
Count++;
}
}[Test]는 한 번에 끝나는 NUnit test에 씁니다. 생성자·계산·상태 전이처럼 Unity frame을 기다릴 이유가 없는 규칙은 이 형태로 두면 가장 빠르고 실패 위치가 짧습니다.[UnityTest]는IEnumerator를 반환하고yield로 frame·coroutine·editor update를 기다릴 수 있습니다. PlayMode에서는 player loop coroutine, EditMode에서는EditorApplication.update기반으로 실행되므로,yield return null하나가 같은 의미의 wall-clock delay가 아닐 수 있습니다.- fixture가 만든 GameObject, asset, static event, SceneManager state는 test 끝에 정리합니다. 이전 test가 남긴 singleton·PlayerPrefs·scriptable state가 다음 test를 통과시키면 test 순서에 따라 결과가 달라집니다.
- parameterized test,
SetUp/TearDown, category는 반복 규칙과 fixture lifecycle을 분리할 때 씁니다. setup에 scene load와 긴 wait를 넣는 대신, 필요한 fixture만 PlayMode helper로 만듭니다.
CI와 실패 판독
bash
"$UNITY" -runTests -batchmode -projectPath "$PROJECT" \
-testPlatform EditMode -testResults "$RESULTS/editmode.xml" \
-logFile "$LOGS/editmode.log"
"$UNITY" -runTests -batchmode -projectPath "$PROJECT" \
-testPlatform PlayMode -testResults "$RESULTS/playmode.xml" \
-logFile "$LOGS/playmode.log"-testPlatform은 실행 범위를 고르고-testResults는 runner 결과를 XML로 씁니다. EditMode와 PlayMode를 분리하면 failure, 실행 시간, domain reload 문제를 바로 구분할 수 있습니다. unity version과 Test Framework version도 CI 결과에 기록합니다.- command exit code만 보지 말고 result XML의 failure message와 Editor log를 함께 보관합니다. compile error, package resolve 오류, test assertion failure는 같은 non-zero여도 고칠 위치가 다릅니다.
-batchmode는 수동 UI 입력을 없애지만 scene-dependent test의 nondeterminism을 고치지 않습니다. time scale, random seed, network, real clock, 이전 scene state가 결과에 영향을 주는지 테스트에서 명시적으로 통제합니다.
자주 틀리는 부분
테스트가 어렵다는 이유로 모든 검증을 PlayMode로 밀지 마세요. 느린 runner와 scene setup은 원인을 흐립니다. MonoBehaviour 밖으로 뺄 수 있는 규칙은 EditMode [Test]로 먼저 닫고, 실제 Unity 연결만 PlayMode로 올립니다.
test asmdef가 Runtime을 참조하는 방향을 반대로 만들지 마세요. Runtime이 test·NUnit·UnityEditor에 의존하면 player build와 컴파일 경계가 오염됩니다. asmdef의 Test Assemblies와 platform filter부터 확인하세요.
참고 링크
3 sources