Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6장 테스트! 개발자의 든든한 지원군 #257

Closed
Tracked by #216
fkdl0048 opened this issue Jul 13, 2024 · 0 comments
Closed
Tracked by #216

6장 테스트! 개발자의 든든한 지원군 #257

fkdl0048 opened this issue Jul 13, 2024 · 0 comments

Comments

@fkdl0048
Copy link
Owner

fkdl0048 commented Jul 13, 2024

6장 테스트! 개발자의 든든한 지원군

업무 부하를 낮추면서 시스템 동작도 검증하는 테스트 방안

테스트를 작성하고 실행하고 수정하는 일은 별반 쓸모없는 작업처럼 보일 수 있다. 실상 테스트는 쓸모없는 일이 되기 쉽다. 좋지 않은 테스트는 아무런 효과도 거두지 못하고 개발자의 업무 부하만 가중시키며 테스트 스위트의 불안정성만 증가시킬 뿐이다.

공감하는 말이며, 의미없는 테스트는 오히려 프로젝트에 악 영향이 간다. 기본적으로 테스트 코드가 필요한 이유와 명세가 명확해야 한다.

테스트 꼭 해야 할까

테스트 코드는 표면적으론 코드의 동작을 확인하는 용도이지만, 실제론 더 다양한 목적이 있다. 의도지 않게 코드의 동작이 바뀌는 것을 방지하고 깔끔한 코드를 작성하게 도와준다. 개발자가 자신의 API를 사용하도록 강제하며 컴포넌트를 어떻게 사용하는지에 대한 문서 역할을 한다.

그외에도 더 구조적인 코드를 짜게 해주거나 방어적인 코드를 작성하게 도와준다.

가장 좋은 점은 역시 테스트 코드가 개발자의 역할을 대신하기에 시간이 지남에 따라 변경에 대한 안전성을 보장해준다. 또한 개발자는 테스트를 작성하면서 프로그램의 인터페이스와 실제 구현에 대해 고민해볼 수 있다.

이 부분이 테스트 코드가 주는 가장 좋은 학습효과라고 생각한다.(학습 효과중에)

이 과정에서 스파게티 코드의 문제점을 발견하거나 미숙한 인터페이스 나아가 TDD까지 고민해볼 수 있다.

테스트의 유형과 기법

테스트의 유형과 기법은 매우 다양하여 따로 책을 보는 것이 좋다고 생각한다. 기본적인 단위 테스트, 통합 테스트, 시스템 테스트, 성능 테스트, 인수 테스트 등이 있으며, 게임 엔진에서는 또 다른 형태의 테스트가 필요하다.

기본적인 단위 테스트는 메서드나 동작 하나를 검증한다. 단위 테스트 특성상 속도가 중요하다. 통합 테스트는 여러 객체의 인스턴스를 생성해서 서로 의존하며 동작하는 코드를 테스트하기 위한 코드이다. (컴포넌트 테스트도 여기에 해당된다고 생각)

시스템 테스트는 시스템 전체를 검증한다. 종단 간 워크플로는 프로덕션 환경의 전반적인 단계에서 실제 사용자의 동작 시뮬레이션한다. 시스템 테스트를 자동화하는 방법은 다양하다. 어떤 조직에서는 릴리스 전에 시스템 테스트를 모두 통과하도록 규정해둔다.

성능 테스트는 주어진 설정하에서 시스템의 성능을 측정하는 것으로, 예를 들어 부하 테스트나 스트레스 테스트가 있다. 서버나, 게임의 사양, 그래픽 환경등 다양한 요소로 성능 테스트 진행

이 외에도 게임에선 QA도 일종의 테스트로 볼 수 있다. 게임의 플레이 테스트, 릴리즈 테스트, 릴리즈 후 모니터링 등이 있다.

테스트 도구

모킹과 같은 테스트 작성 도구를 사용하면 더 효율적인 테스트를 작성할 수 있다. 코드 품질 도구는 커버리지와 복잡도 등을 분석하거나 정적 분석을 통한 에러, 코드 스타일 등을 검사한다.

하지만 도구를 추가함으로써 그만큼의 엔트로피는 증가하니 잘 생각해야 한다. 도구의 일원화는 중요하다.

모킹 라이브러리

과거 TDD를 공부하며 C#의 모의 객체를 만드는 라이브러리인 NSubstitute를 사용해봤다. 인터페이스를 모방하여 해당 메서드가 호출되었는지, 호출되었을 때 어떤 값을 반환해야 하는지 등을 정의할 수 있다.

이를 통해 복잡한 의존성을 가진 코드를 테스트할 때 테스트 대상 코드가 의존하는 객체를 모킹하여 테스트를 진행할 수 있다.

하지만 모의 객체에 과도하게 의존한다는 것은 결국 코드가 강하게 결합되어 있음을 의미하기 때문에 결국 악취다. 이때는 의존성을 제거하도록 리팩터링을 해야한다.

테스트 프레임워크

테스트 프레임워크는 테스트 코드를 작성하고 실행하는 도구이다. C#에서는 NUnit, xUnit을 써봤지만 결국 엔진이라는 더 상위의 개념에 의존적일 수 밖에 없다. 따라서 유니티나 언리얼 자체에서 제공하는 테스트 프레임워크를 사용하는 것이 좋다.

코드 품질 도구

요즘에는 대부분 IDE가 많이 좋아져서 책에서 나오는 기능들이 다 IDE에 포함되지 않을까 싶다. 코드 복잡도 도구나 이를 시각화해주는 도구가 따로 있다면 너무 좋을 것 같다.

개발자 스스로 직접 테스트를 작성하자

누군가가 내 코드를 대신 정리해줄 것이라 기대하지 말고 직접 테스트를 작성하자.

테스트는 깔끔하게 작성하자

다른 코드와 마찬가지로 오히려 더욱 테스트 코드는 깔끔하게 작성해야 한다. 테스트 코드 그 자체로 문서이기 때문이다. 상세 구현보다는 근본적인 기능을 테스트하는 것이 중요하다. 또한 테스트에 필요한 의존성은 실제 코드의 의존성과 분리해서 관리하자.

과도한 테스트는 삼가자

테스트를 작성하는 데 너무 많은 노력을 기울이지는 말자. 테스트를 너무 많이 작성하면 어떤 테스트가 꼭 필요한 것인지에 대한 감각을 잃어버리기 쉽다.

위에서 말한 것처럼 세세한 내용보단 전체적인 맥락에 대한 테스트가 더 중요하다..

커버리지 지표를 올리기 위한 테스트 코드를 작성하지는 말자. 커버리지가 높다고 좋은 소프트웨어인 것은 아니다.

테스트 결정성: 항상 동일한 테스트 결과를 만드려면

결정적 코드란 입력이 같으면 그 출력도 항상 같은 코드를 말한다. 반면 비결정적 코드는 입력이 같아도 출력은 다를 수 있다. 마치 수학의 연속함수와 불연속함수처럼 말이다.

비결정적 코드는 테스트의 가치를 떨어뜨린다. 테스트가 간헐적으로 실패한다는 것은 매번 동일한 현상이 발생하지는 않는다는 뜻이므로 재현과 디버깅이 어렵다. 코드가 문제인지 테스트가 문제인지 알 수 없기 때문이다. 즉, 테스트하려는 도메인도 결정적이어야 한다.

테스트의 실행 순서에 의존하지 말자

테스트를 실행 순서에 의존해서는 안 된다. 실제 게임에서는 많이 발생하는 경우다. 의존되어야 하는 테스트는 하나로 묶어서 관리할 것..! 이는 싱글톤을 사용하면 자주 발생하는 문제이다. 이를 위해 공통되는 작업을 Setup 메서드로 빼서 관리하자.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

No branches or pull requests

1 participant