From c502b0e0f2c0586761e7ed39a3b2c5e08d48f20e Mon Sep 17 00:00:00 2001 From: Jeongan Lee <84510455+fkdl0048@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:04:40 +0900 Subject: [PATCH] Docs: Add The_Missing_README/Chapter06.md --- The_Missing_README/Chapter06.md | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 The_Missing_README/Chapter06.md diff --git a/The_Missing_README/Chapter06.md b/The_Missing_README/Chapter06.md new file mode 100644 index 0000000..9ac3009 --- /dev/null +++ b/The_Missing_README/Chapter06.md @@ -0,0 +1,79 @@ +## 6장 테스트! 개발자의 든든한 지원군 + +> 업무 부하를 낮추면서 시스템 동작도 검증하는 테스트 방안 + +테스트를 작성하고 실행하고 수정하는 일은 별반 쓸모없는 작업처럼 보일 수 있다. 실상 테스트는 쓸모없는 일이 되기 쉽다. 좋지 않은 테스트는 아무런 효과도 거두지 못하고 개발자의 업무 부하만 가중시키며 테스트 스위트의 불안정성만 증가시킬 뿐이다. + +*공감하는 말이며, 의미없는 테스트는 오히려 프로젝트에 악 영향이 간다. 기본적으로 테스트 코드가 필요한 이유와 명세가 명확해야 한다.* + +### 테스트 꼭 해야 할까 + +테스트 코드는 표면적으론 코드의 동작을 확인하는 용도이지만, 실제론 더 다양한 목적이 있다. 의도지 않게 코드의 동작이 바뀌는 것을 방지하고 깔끔한 코드를 작성하게 도와준다. 개발자가 자신의 API를 사용하도록 강제하며 컴포넌트를 어떻게 사용하는지에 대한 문서 역할을 한다. + +*그외에도 더 구조적인 코드를 짜게 해주거나 방어적인 코드를 작성하게 도와준다.* + +가장 좋은 점은 역시 테스트 코드가 개발자의 역할을 대신하기에 시간이 지남에 따라 변경에 대한 안전성을 보장해준다. 또한 **개발자는 테스트를 작성하면서 프로그램의 인터페이스와 실제 구현에 대해 고민해볼 수 있다.** + +*이 부분이 테스트 코드가 주는 가장 좋은 학습효과라고 생각한다.(학습 효과중에)* + +이 과정에서 스파게티 코드의 문제점을 발견하거나 미숙한 인터페이스 나아가 TDD까지 고민해볼 수 있다. + +### 테스트의 유형과 기법 + +테스트의 유형과 기법은 매우 다양하여 따로 책을 보는 것이 좋다고 생각한다. 기본적인 단위 테스트, 통합 테스트, 시스템 테스트, 성능 테스트, 인수 테스트 등이 있으며, 게임 엔진에서는 또 다른 형태의 테스트가 필요하다. + +기본적인 `단위 테스트`는 메서드나 동작 하나를 검증한다. 단위 테스트 특성상 속도가 중요하다. `통합 테스트`는 여러 객체의 인스턴스를 생성해서 서로 의존하며 동작하는 코드를 테스트하기 위한 코드이다. (`컴포넌트 테스트`도 여기에 해당된다고 생각) + +`시스템 테스트`는 시스템 전체를 검증한다. 종단 간 워크플로는 프로덕션 환경의 전반적인 단계에서 실제 사용자의 동작 시뮬레이션한다. 시스템 테스트를 자동화하는 방법은 다양하다. 어떤 조직에서는 릴리스 전에 시스템 테스트를 모두 통과하도록 규정해둔다. + +`성능 테스트`는 주어진 설정하에서 시스템의 성능을 측정하는 것으로, 예를 들어 부하 테스트나 스트레스 테스트가 있다. 서버나, 게임의 사양, 그래픽 환경등 다양한 요소로 성능 테스트 진행 + +이 외에도 게임에선 QA도 일종의 테스트로 볼 수 있다. 게임의 플레이 테스트, 릴리즈 테스트, 릴리즈 후 모니터링 등이 있다. + +### 테스트 도구 + +모킹과 같은 테스트 작성 도구를 사용하면 더 효율적인 테스트를 작성할 수 있다. 코드 품질 도구는 커버리지와 복잡도 등을 분석하거나 정적 분석을 통한 에러, 코드 스타일 등을 검사한다. + +하지만 도구를 추가함으로써 그만큼의 엔트로피는 증가하니 잘 생각해야 한다. 도구의 일원화는 중요하다. + +#### 모킹 라이브러리 + +과거 TDD를 공부하며 C#의 모의 객체를 만드는 라이브러리인 `NSubstitute`를 사용해봤다. 인터페이스를 모방하여 해당 메서드가 호출되었는지, 호출되었을 때 어떤 값을 반환해야 하는지 등을 정의할 수 있다. + +이를 통해 복잡한 의존성을 가진 코드를 테스트할 때 테스트 대상 코드가 의존하는 객체를 모킹하여 테스트를 진행할 수 있다. + +하지만 모의 객체에 과도하게 의존한다는 것은 결국 코드가 강하게 결합되어 있음을 의미하기 때문에 결국 악취다. 이때는 의존성을 제거하도록 리팩터링을 해야한다. + +#### 테스트 프레임워크 + +테스트 프레임워크는 테스트 코드를 작성하고 실행하는 도구이다. C#에서는 `NUnit`, `xUnit`을 써봤지만 결국 엔진이라는 더 상위의 개념에 의존적일 수 밖에 없다. 따라서 유니티나 언리얼 자체에서 제공하는 테스트 프레임워크를 사용하는 것이 좋다. + +#### 코드 품질 도구 + +요즘에는 대부분 IDE가 많이 좋아져서 책에서 나오는 기능들이 다 IDE에 포함되지 않을까 싶다. 코드 복잡도 도구나 이를 시각화해주는 도구가 따로 있다면 너무 좋을 것 같다. + +### 개발자 스스로 직접 테스트를 작성하자 + +누군가가 내 코드를 대신 정리해줄 것이라 기대하지 말고 직접 테스트를 작성하자. + +#### 테스트는 깔끔하게 작성하자 + +다른 코드와 마찬가지로 오히려 더욱 테스트 코드는 깔끔하게 작성해야 한다. 테스트 코드 그 자체로 문서이기 때문이다. **상세 구현보다는 근본적인 기능을 테스트하는 것이 중요하다.** 또한 테스트에 필요한 의존성은 실제 코드의 의존성과 분리해서 관리하자. + +#### 과도한 테스트는 삼가자 + +테스트를 작성하는 데 너무 많은 노력을 기울이지는 말자. **테스트를 너무 많이 작성하면 어떤 테스트가 꼭 필요한 것인지에 대한 감각을 잃어버리기 쉽다.** + +*위에서 말한 것처럼 세세한 내용보단 전체적인 맥락에 대한 테스트가 더 중요하다..* + +**커버리지 지표를 올리기 위한 테스트 코드를 작성하지는 말자.** 커버리지가 높다고 좋은 소프트웨어인 것은 아니다. + +### 테스트 결정성: 항상 동일한 테스트 결과를 만드려면 + +`결정적 코드`란 입력이 같으면 그 출력도 항상 같은 코드를 말한다. 반면 `비결정적 코드`는 입력이 같아도 출력은 다를 수 있다. 마치 수학의 연속함수와 불연속함수처럼 말이다. + +비결정적 코드는 테스트의 가치를 떨어뜨린다. 테스트가 간헐적으로 실패한다는 것은 매번 동일한 현상이 발생하지는 않는다는 뜻이므로 재현과 디버깅이 어렵다. 코드가 문제인지 테스트가 문제인지 알 수 없기 때문이다. 즉, 테스트하려는 도메인도 결정적이어야 한다. + +#### 테스트의 실행 순서에 의존하지 말자 + +**테스트를 실행 순서에 의존해서는 안 된다.** 실제 게임에서는 많이 발생하는 경우다. 의존되어야 하는 테스트는 하나로 묶어서 관리할 것..! 이는 싱글톤을 사용하면 자주 발생하는 문제이다. 이를 위해 공통되는 작업을 Setup 메서드로 빼서 관리하자.