[Unity] using NUnit.Framework;
![[Unity] using NUnit.Framework;](/_next/image?url=https%3A%2F%2Fdata.develog.develrocket.com%2Fupload%2Fdevelog%2Fuser_1776858831954%2F1785510093345-nm1501%2F_____.png&w=3840&q=75)
EditMode Test Report
- 에디터에서 바로 실행 가능한 테스트

PlayMode Test Report
- 플레이 모드 진입해야되는 테스트

들어가며
복선이 있긴 했다

나는 System.Collection의 리스트를 쓰고 싶었는데, 자동완성이 NUnit.Framework의 List를 찾아서 원하지 않은 using문이 생겼던 기억이 자주 있다. 당연히 이 네임스페이스에 대해서는 전혀 아는 바가 없었다.
"검증기를 만들어서 시연할 것"
곧 기업협약 프로젝트가 시작된다. 우리 팀에 주어진 주요 과제를 해결하기 위해서는, 게임 메카닉이 결정론적으로 동작하는지 검증하는 프로그램을 만들어야 했다. 이를 위한 검증기가 필요해졌고, 그렇게 NUnit.Framework를 다시 만났다.
NUnit.Framework 는 무엇인가?
개요
이 네임스페이스는 단위 테스트를 도와주는 순수 C#의 네임스페이스다.
단위 테스트란, 주로 메서드 하나 단위의 작은 코드 단위가 의도대로 동작하는지 확인하는 것을 의미한다.
???: 그냥 PlayMode 진입해서 눈으로 확인하면 안 돼요?
안됩니다.
장점
- 빠름: 빌드 배치 툴을 만드는 방법을 배웠듯이, 같은 방법으로 테스트용 배치툴을 만들어 여러 테스트 프로세스를 병렬로 돌릴 수 있다.
- 반복 가능: 동일한 조건에서 동일한 결과를 낸다. 이 부분에서는 결정론적이다.
- 회귀 예방: 형상관리로 협업하다보면 종종 '어? 왜 예전 버전으로 돌아갔지?' 가 생기는데, 이를 예방한다.
Unity Test Framework(UTF)
Unity는 자체적인 테스트 도구가 있으나, 이는 NUnit을 Unity에 맞게 커스터마이즈하여 제공되는 것이다. 즉, 우리가 Unity에서 쓰는 NUnit이 UTF다.
주요 어트리뷰트
어트리뷰트 | 역할 |
|---|---|
[Test] | 메서드에 붙는다. 이 메서드는 테스트용이다. |
[TestFixture] | 유니티에서는 생략 가능하다. 이 클래스가 테스트 메서드를 포함함을 의미한다. |
[SetUp] | 각 테스트 실행 직전에 호출된다. |
[TearDown] | 각 테스트 실행 직후에 호출된다. |
[TestCase(...)] | 하나의 테스트를 여러 입력값으로 반복 실행한다. |
[UnityTest] | UTF 전용. 코루틴처럼 yield return으로 프레임을 넘기며 테스트한다. |
주요 Assert API
Assert.* | 역할 |
|---|---|
AreEqual(expected, actual); AreEqual(expected, actual, delta); | 두 값이 같은가? 또는 두 값이 delta의 허용 오차를 두고 같은가?(부동소수점) |
IsTrue(condition); | 참인가? |
IsFalse(condition); | 거짓인가? |
IsNull(obj); | null인가? |
Throws<ArgumentException>(Action action); | action은 예외가 나야 정상인 코드. |
사용법
1. 어셈블리(asmdef) 만들기
어셈블리는 프로젝트 폴더 내에 .asmdef 파일로 만들 수 있고, 이 파일은 해당 모듈(네임스페이스)이 참조할 수 있는 모듈이나 규칙(unsafe 허용 등)을 정의한다. UTF는 NUnit을 참조하는 어셈블리 내에서만 작동하므로, 이 파일을 생성해야한다. 당연히 테스트를 위해 참조해야할 게임 메카닉이 정의된 모듈도 참조를 명시할 필요가 있다.
2. Test Runner 창 열기
Window > General > Test Runner 에서 위의 어셈블리 파일을 자동 생성 할 수 있다.
- EditMode 테스트: 에디터에서 즉시 실행된다. 순수 C# 로직 검증에 적합하다.
- PlayMode 테스트: 실제 플레이 루프 속에서 실행된다. MonoBehaviour, 프레임 진행, 물리 등 엔진이 관여해야할 때 사용한다.
사실 진짜 체감되는 장점은 따로 있음
나 같은 신입도 데이터 모델과 POCO 로직, MonoBehaviour 사이클 등을 분리할 수 있도록 도와주는 기준이 된다.
만약 기능 구현은 다 됐는데 단위 테스트가 가능한 형태가 아니라면, 뭔가 냄새가 날 것이다.
주의해야할 점
각 테스트가 독립되어있으면서도 결정론적이어야한다.
이 명제가 훼손되면 테스트 자체가 성립할 수 없다.
- 따라서 static 필드나 싱글턴에의 의존은 실행 순서에 따라 결과가 바뀔 수 있어 조심해야한다.
- 의도한 에러라면 Debug.LogError 쓰지 말고 LogAssert.Expert 쓰자. LogError는 테스트가 실패처리된다.
- float 비교에는 허용 오차를 포함한다
- 테스트를 잘게 쪼갠다. Assert가 많아지면 테스트 실패시 원인 파악이 귀찮다.

![[Unity] 제네릭과 가변성(variance)](https://data.develog.develrocket.com/upload/develog/gmumdev/1791297148427-udtfz5/_____.png)