Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
테스트 주도 개발이란?
게임을 개발해 보면서 점점 많아지는 코드량에, 함부로 특정 함수를 변경했다가 양산될 버그가 두려워지기 시작했다. 덩달아 생산성도 떨어지고 뭐부터 만들어야 할지 막막해졌다.
이럴 때 필요한 게 바로 테스트 주도 개발(TDD, Test Driven Development)이라 생각했다.
실무에서 프로그래머들은 실제로 코드를 작성하는 시간보단 설계의 고민, 디버깅에 대부분의 시간을 쏟는다. 특히 디버깅이 얼마나 끔찍한지는 초심자라도 잘 알 것이다. 하지만, 테스트 코드 어떤 부분에서 버그가 발생했는지 한 번에 알 수 있다.
물론 개발에 직접적인 코드를 작성하는 것이 아닌 부가적인 코드를 상당량 작성해야하는 것이기 때문에 부담을 느낄 수 있다. 실제로 (본인도 그렇지만) 테스트가 오히려 개발 속도를 올려주는 것을 직접 경험하지 않고는 진가를 알기 어려울 것이다. 하지만, 구현 전에 테스트를 먼저 작성하면 기능 구현을 위해 무엇이 필요한지 고민하게 되고, 한 번에 하나씩만 집중할 수 있다. 모듈, 클래스, 함수를 구체적으로 정의하도록 강제하여 일도 점진적으로 진행할 수 있다.
특히 테스트 코드는 잘 정리된 요구사항의 역할도 하기 때문에 필요한 만큼만 코딩을 하여 불필요한 오버 엔지니어링을 방지해준다.
즉, 개발 전에 테스트부터 작성하는 것이 테스트 주도 개발(TDD, Test Driven Development)이라 할 수 있다.
TDD는 테스트 작성 -> 테스트를 통과하는 코드 작성 -> 결과 코드를 리팩터링 하는 3단계 과정을 거친다.
따라서 생산성을 높이면서도 차분히 기능을 구현해 나갈 수 있다.
유니티에서 테스트 코드 적용
이번에는 유니티의 C#에서 테스트를 진행하는 방식을 알아보자.
코드를 테스트 할때는 해당 언어의 테스트 프레임 워크를 사용하는데 대표적인 예가 자바의 JUnit이다.
일반적으로 C#은 visual studio에서 제공하는 테스트 프레임 워크를 사용하지만, 유니티에서도 테스트 프레임 워크를 제공한다.
Test runner의 Create PlayMode Test Assembly Folder로 원하는 위치에 Test 폴더를 만들고 Create test Script in current floder 버튼으로 테스트 스크립트를 생성해주자.
TestScript에 들어가면 기존의 코드를 전혀 참조할 수 없는데 이는 테스트 코드와 어셈블리가 다르기 때문이다.(자바의 패키지와 비슷한 개념으로 보면 된다.)
(자세한 내용은 링크를 참조: https://docs.unity3d.com/kr/2021.2/Manual/ScriptCompilationAssemblyDefinitionFiles.html)
테스트 어셈블리에 기존의 코드의 어셈블리를 참조하기 위해서 다음과 같이 기존 코드의 어셈블리 파일을 생성해주자.
이제 Tests 폴더에서 생성되어 있는 Assembly Definition에 방금 생성한 Assembly Definition을 references에 추가해주자.
이때 UnityEngine.Rendering.PostProcessing을 기존에 사용 중이었다면 PostProcessing에 대해 아래의 오류가 발생할 수 있다.
are you missing a using directive or an assembly reference?
이는 기존 코드로 생성한 assembly definition에 PostProcessing assembly reference가 추가되어 있지 않기 때문이다.
따라서 references에 위처럼 두가지 definition을 찾아서 추가해주자.
이제 TestScript에서도 기존 코드의 클래스와 함수를 사용할 수 있다.
이번에 테스트 해볼 함수는 두 변수의 값을 바꾸어 주는 함수 Swap()이다.
1
2
3
4
5
6
7
8
9
10
|
public class Utils
{
public static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
}
|
cs |
swap함수가 제대로 동작한다면 매개변수로 보낸 두 값이 바뀌어 저장되어야 한다.
이를 위한 소스 코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class TestScript
{
// A Test behaves as an ordinary method
[Test]
public void TestUtilsSwap()
{
// Use the Assert class to test conditions
int a = 10, b = 20;
int a_value = a, b_value = b;
Utils.Swap<int>(ref a, ref b);
Assert.AreEqual(a, b_value);
Assert.AreEqual(b, a_value);
}
}
|
cs |
Test 코드에서 사용하는 Assert클래스는 결괏값을 비교해 테스트의 성공 실패 여부를 판단해주는 다양한 함수를 지원한다.
이번에 사용한 Assert.AreEqual() 함수는 두 매개변수 값이 같은지 확인해준다.
두 값이 같가면 아래와 같이 Test runner에서 run all 버튼으로 모든 테스트를 실행하거나 개별 함수를 실행하여 결과를 확인해 볼 수 있다.
만약 기능을 구현한 함수가 미리 기대한 결과로 만들어 놓은 테스트 코드와 다른 값을 낸다면 다음과 같이 어떤 함수에 어떤 값이 다르게 나오는지 바로 확인해 볼 수 있다.
이번에는 간단히 유니티에서의 테스트 코드 활용법을 알아보았는데, 테스트 주도 개발에는 다양한 기법의 테스트 작성 방법이 있으니 공부해보면서 차근차근 적용해 나가 보자.
참조 서적: 리펙터링 2판 (마틴 파울러 저), 소프트웨어 장인 (산드로 만쿠소 저)
'개발 > Unity' 카테고리의 다른 글
[유니티] IPointerHandler 인터페이스가 작동하지 않는 이유 (0) | 2022.03.07 |
---|---|
[Unity] C# enum(열거형) 활용하기 (0) | 2022.02.08 |
[유니티] Coroutine으로 Update 대체하기 (Update 최적화) (0) | 2022.01.18 |
[유니티] Instantiate로 인한 Awake와 Start의 차이 확인하기 (0) | 2022.01.15 |
[Unity] 유니티에서의 다중 상속과 다이아몬드 문제 (2) | 2022.01.05 |