단위 테스트
단위 테스트는 테스트 가능한 소프트웨어의 가장 작은 부분을 실행하여 예상대로 작동하는지 확인하는 것입니다. 즉, 시스템의 소스 코드 로직 등을 테스트로 점검하는 단계로 주로 소스 코드의 클래스나 메서드 단위의 기능이 정상적으로 동작하는지 검증합니다. 테스트 중인 단위가 작을수록 단위 테스트를 사용하여 동작을 표현하는 것이 쉬워지므로 단위의 분기 복잡성이 낮아집니다.
개발 단계에서 개발자 또는 개발팀 차원에서 직접 수행을 하여 자신이 코딩한 메서드나 클래스가 정상적으로 동작하는지를 테스트하는데, 주로 기능이 제대로 작동하는지를 점검합니다. 즉, 그 결과가 기대값과 일치하는지 확인하는 형태이며, 서비스 호출, 외부 파일/네트워크 연결 없이 서로 독립적으로 수행되어야 합니다.
단위 테스트는 버그를 찾는 게 목적이 아니라 설계 프로세스의 일부로 생각해야 하지만, 개발자가 직접 단위 테스트 코드를 구현해야 하기 때문에 개발자에 대한 부담이 가중됩니다. 특히 단위 테스트를 꼼꼼하게 구현할수록 단위 테스트 구현시간이 늘어나므로 가장 이상적인 케이스는 코딩 시간과 단위 테스트 구현 시간을 같이 고려하여 개발 일정을 짜고,
모듈을 개발한 개발자가 단위 테스트까지 같이 작성하게 하는 것이 효율성이 높습니다.
단위 테스트를 잘하기 위한 고려 사항
컨설턴트 라쎄 코스켈라는 「Effective Unit Testing」 책에서 단위 테스트를 잘하기 위한 고려사항을 언급했습니다. 유지 보수하기 쉬운 가독성 있는 코드 및 고수준 개념을 빠르고 정확하게 대입할 수 있는 코드로 구조화되어야 하고, 작성한 테스트는 정확한 대상을 검사해야 하며, 테스트가 다른 코드와 종속성 없이 테스트 가능한지가 중요합니다.
독립 단위 테스트, 연동 단위 테스트
단위 테스트는 테스트 가능한 가장 작은 단위를 독립된 환경에서 테스트하는 것이지만, 일반적으로 소프트웨어 컴포넌트는 혼자서 동작하기보다는 타 컴포넌트에 의존성을 가지기 때문에 의존성이 있는 컴포넌트가 완성되지 않았거나 오류가 있으면 정상적으로 테스트를 할 수 없습니다.
그래서 테스트 진행 방식을 2가지 종류로 나눌 수 있는데, 독립 단위 테스트와 연동 단위 테스트입니다. 독립 단위 테스트는 테스트 객체와 의존성이 있는 부분을 테스트 더블 등의 기능으로 대체하여 테스트하는 방식으로 다른 객체와 독립적으로 테스트합니다.
연동 단위 테스트는 객체 상태 변화를 관찰하여 모듈의 동작을 테스트하는데 초점을 맞춥니다. 테스트 중인 유닛은 인터페이스를 통해 완전히 테스트된 블랙박스로 취급합니다.
단위 테스트 방식 - 도메인
객체 지향 및 도메인 중심 설계에서 볼 수 있는 도메인 로직은 종종 복잡한 계산을 하거나 상태가 전이되는 등 합쳐진 형태입니다. 그러므로 단위를 분리하는 것은 큰 의미가 없습니다. 그러므로 가능한 한 테스트 대상과 연관된 도메인 객체들을 함께 테스트하는 것이 좋습니다.
단위 테스트 방식 - 게이트웨이, 리포지토리
모듈 연동 코드를 사용하면 테스트 중인 유닛을 외부 모듈과 분리 후 상태 변경을 테스트하는 것이 어렵기 때문에 테스트 더블 같은 기능을 사용하는 것이 효과적입니다. 테스트하는 주목적은 통합적인 연동 확인보다는 요청을 생성하거나 외부 종속성의 응답을 매핑하는 데 사용된 모든 로직을 확인하는 데 있습니다.
즉, 외부 의존적인 부분을 테스트 더블로 대체해 빠른 반복 요청/응답을 피드백 받고, 특정 오류도 재현하여 테스트를 할 수 있습니다.
단위 테스트 방식 - 리소스, 서비스 레이어
코디네이션 로직은 모듈 안 복잡한 로직보다 모듈 내에서 전달되는 메시지를 더 중요하게 다룹니다. 테스트 더블을 통해 전달 된 메시지의 세부 내용을 검증하고 모듈에서 통신 응답을 스텁화 할 수 있습니다. 만약 코디네이션 로직에 너무 많은 테스트 더블이 필요하다면, 일부 개념을 추출 및 분리하여 테스트해야 합니다.
단위 테스트 방식 - 기타
서비스의 크기가 줄어들수록 복잡한 도메인 로직 관련 연동 및 코디네이션 로직이 증가하는 경향을 보입니다. 마찬가지로 일부 서비스에는 다른 기술에 대한 어댑터 또는 다른 서비스에 대한 Aggregator와 같은 전체 연동 및 코디네이션 로직이 있을 수 있는데, 이 경우 포괄적인 단위 테스트보다는 컴포넌트 테스트와 같은 다른 테스트가 효과가 좋을 수 있습니다. 즉, 단위 테스트는 단위의 동작을 제한하고 시스템 동작에 대한 보장을 제공하지 않으므로 보다 큰 범위의 테스트를 반드시 필요로 합니다.
테스트 더블
제라드 메사로스(Gerard Meszaros)가 xUnit Test Patterns에서 테스트 더블이라는 용어를 언급했습니다. 여기서 '더블'은 보통 액션 영화 등의 스턴트 대역을 뜻하는 단어로, 테스트 더블이란 테스트 시에 실제 객체를 대신 할 수 있는 객체, 다시 말해 일반적으로 테스트 대상 코드와 협력 객체를 분리해서 테스트하기 때문에 테스트 작성 시 테스트 대상 코드와 상호 작용하는 객체를 말합니다. 보통 소프트웨어 컴포넌트는 혼자서 동작할 수 없고 다른 컴포넌트에 대해서 종속성(Dependency)을 가지고 있기 때문에 종속 관계에 있는 컴포넌트가 완성되지 않거나 그 컴포넌트에 오류가 있으면 정상적으로 테스트를 진행할 수가 없는데, 이런 경우 가상의 테스트용 클래스와 메서드를 구현하게 됩니다. 이를 목업(Mock-Up) 클래스라고 합니다.
테스트 케이스 작성 시 일반적인 테스트 더블은 상태에 중점을 두고, 목 객체는 행위에 중점을 둡니다. 중요한 것은 어떤 테스트 더블을 쓰는 게 중요한 게 아니라 무엇을 대체할 것인지, 또 반드시 필요한지 고민해야 합니다. 즉, 목 같은 오브젝트는 개발 및 사용 전에 과연 효과적인지 판단해 보고 사용해야 합니다.
테스트 단계
테스트를 진행하는 단계에 있어서 전통적인 xUnit 테스트 패턴에서는 Setup/Exercise/Verify/Teardown의 4단계로 구분했고, 즉, BDD에서는 Given/When/Then으로 구분했으며, Bill Wake는 Arrange/Act/Assert로 구분하기도 했습니다.
각각의 테스트 패턴을 아는 것도 중요하겠지만 테스트를 하기 위한 기본 개념에 익숙해지는 것이 중요합니다. 단위 테스트의 메서드 이름을 명명할 때 많이 쓰이는 명명 규칙들입니다. 팀 내에서 개발 표준을 정할 때 각각의 장단점을 파악 후 사용하시기 바랍니다.
'SW > DevOps' 카테고리의 다른 글
DevOps : Service Test 종류 및 방법 (0) | 2019.12.18 |
---|---|
DevOps : UnitTest와 JUnit 개념 및 사용법 (0) | 2019.12.17 |
DevOps : SW 테스트 개요 (0) | 2019.12.15 |
DevOps : 리팩토링 기법 (3) (0) | 2019.12.14 |
DevOps : 리팩토링 기법 (2) (0) | 2019.12.13 |