3. 소프트웨어 개발 단계와 테스트
- 테스트 레벨에 따른 테스트 분류
- 테스트 설계 방식에 따른 분류
- 테스트 유형(품질 특성)에 따른 분류
테스트 레벨
- 컴포넌트/단위 테스트 : 각각의 컴포넌트를 테스트
- 통합 테스트 : 컴포넌트 간의 인터페이스를 테스트
- 시스템 테스트 : 전체 시스템이 목적을 만족시키는지 테스트
- 인수 테스트 : 사용자의 요구사항을 만족하는지 확인
테스트 설계 - 동적 테스트
- 명세 기반 테스트 : 명세를 바탕으로 테스트 케이스 생성
- 구조 기반 테스트 : 프로그램 코드 정보를 바탕으로 테스트 케이스 생성
- 경험 기반 테스트 : 테스터의 경험을 기반으로 테스트 케이스 생성
테스트 설계 - 정적 테스트
- 리뷰 : 산출물에 존재하는 결함을 검출하거나 프로젝트의 진행 상황을 점검
- 정적 분석 : 자동화된 도구를 이용하여 산출물의 결함을 검출하거나 복잡도를 측정
테스트 유형 - 기능 테스트
- 기능적 요구사항 측면의 결함 검출 및 충족 여부를 확인
테스트 유형 - 비기능 테스트
- 기능 적합성 테스트 : 사용자의 요구사항을 만족하는 기능이 제공되는 정도를 테스트
- 성능 효율성 테스트 : 시스템 응답시간이나 처리량을 테스트
- 호환성 테스트 : 다른 시스템과의 상호 연동 능력이나 공존성을 테스트
- 사용성 테스트 : 사용자가 이해하고 배우기 쉬운 정도를 테스트
- 신뢰성 테스트 : 규정된 조건/기간에 오작동 없이 수행하는 능력을 테스트
- 보안성 테스트 : 시스템의 정보 및 데이터를 보호하는 능력을 테스트
- 유지보수성 테스트 : 소프트웨어 유지보수의 용이성을 테스트
- 이식성 테스트 : 다양한 플랫폼에서 운영될 수 있는 능력을 테스트
컴포넌트 테스트
- 개별적인 모듈에 대한 컴포넌트 테스트를 진행하기 위해서 모듈을 단독으로 실행할 수 있는 환경 필요
- 테스트 환경은 테스트 베드라고도 함
- 테스트 환경의 주요 구성 요소로 테스트 드라이버와 테스트 스텁이 있음
- 컴포넌트 테스트는 시스템을 구성하는 컴포넌트를 독립적인 방식으로 테스트를 수행하는 것이 특징
- 컴포넌트 0을 테스트할 때 컴포넌트 1과 2에 의존하지 않는 방식으로 수행
- 컴포넌트 1을 대상으로 테스트할 때 컴포넌트 2 대신 스텁을 이용
- 개별적인 컴포넌트의 독립적인 동작을 확인
모의 객체 생성 프레임워크
- 독립적인 컴포넌트 테스트를 위해 절차 지향적 프로그래밍에서 스텁과 같은 개념이 필요
- 모의(Mock) 객체는 스텁의 객체 지향 버전이라고 할 수 있음
- 모의 객체는 개발자가 처음부터 수작업으로 만들거나 모의 객체 생성 프레임워크를 이용하여 만들 수 있음
모의 객체 - 더미(dummy) 객체 : 테스트할 때 객체만 필요하고 해당 객체의 기능까지는 필요하지 않은 경우 사용 - 테스트 스텁 : 더미 객체에 단순한 기능성을 작성하여 추가 객체의 특성 상태를 가정해서 특정 값, 메시지를 반환 - 테스트 스파이 : 주로 테스트 대상 클래스(CUT)와 협력하는 클래스로 가는 출력을 검증하는데 사용 CUT가 실행되는 동안 특정 협력 클래스로의 호출이나 결과를 잡아내 실행 끝난 후 정상호출인지 검사 - 가짜 객체 : 실제 협력 클래스의 기능을 대체해야 할 경우에 사용, 실제 협력 클래스의 기능 중 전체나 일부를 간단히 구현 실제 협력 클래스가 구현되지 않았거나 느리거나 테스트 환경에서 사용할 수 없을 때 사용 |
FIRST 원칙
- 컴포넌트 테스트는 다른 테스트에 비해 테스트 수행에 따른 피드백이 빠름
- 결함 발견 시 결함 발생시킨 부분을 쉽게 식별하여 수정할 수 있음
컴포넌트 테스트를 잘 수행하기 위한 FIRST 원칙이 있음
- Fast : 컴포넌트 테스트의 주요 목적 중 하나가 리그레션 테스트인데, 코드를 변경할 때마다 4분 넘는 테스트가 수행되면 개발자는 테스트를 수행하지 않고자하는 유혹에 빠질 수 있고, 코드에 새로운 기능이 추가될 수록 테스트 실행 시간은 증가될 것. 데이터베이스나 네트워크처럼 외부 자원에 의존하는 경우테스트 수행 시간이 길어질 가능성이 큼. 모의 객체 생성 프레임워크를 이용하여 테스트 시간을 단축하는 것이 좋음
- Isolated : 어떤 특정한 컴포넌트 테스트 집합이나 컴포넌트 테스트 하나를 독립적으로 수행할 수 있어야 함. 한 컴포넌트 테스트가 다른 컴포넌트 테스트의 수행 결과에 의존하면 테스트 실행 순서에 따라 다른 결과가 나올 것. 전체 컴포넌트 테스트 집합을 실행한 결과와 개별적으로 컴포넌트 테스트를 실행한 결과가 다르면 안됨.
- Repeatable : 테스트를 몇 번 실행해도 동일한 결과가 나오도록 해야 함. 데이터베이스 내용이 이미 다른 개발자가 수행한 테스트에 의해 변경될 수 있음. 이 변경으로 인해 테스트의 결과가 달라질 수 있음. 개발자 자신만이 사용할 수 있는 샌드박스를 구축해서 테스트를 수행하는 것이 좋음
- Self-Validating : 사람의 개입 없이 테스트가 통과되었는지 알 수 있도록 작성해야 함. 사람이 개입하도록 하면 많은 시간이 소요됨. 자동화하여 사람이 개입할 필요가 없는 것이 좋음.
- Timely : 제때 수행되어야 함. 테스트 대상이 되는 코드가 작성되는 시점을 의미. TDD에서는 코드 작성 바로 전.
통합 테스트
통합 테스트는 컴포넌트를 통합하는 과정에서 수행되는 테스트
컴포넌트 간의 상호 연동이 제대로 수행되는지 검사하는 테스트
개별적인 컴포넌트에 대해 테스트가 수행되었더라도 실제로 컴포넌트 통합 후 결함이 생길 수 있음
통합 테스트 목적에 대해 두 가지 다른 관점
- 자료에 따라 통합테스트가 두 컴포넌트 간 연결의 정확성에만 초점을 두기도 함
- 연결된 두 컴포넌트의 기능적인 측면에 초점을 두기도 함
상호작용에만 초점을 두고 통합 테스트
- 두 컴포넌트 간에 전송, 수신된 데이터를 중심으로 봄
- output1에서 전송 데이터에 대한 로그를 생성, input2에서 수신 데이터에 대한 로그를 생성하여 두 값을 비교
- 시스템에 따라 전송, 수신 로그를 생성하는 것이 용이하지 않을 수 있음
- 임베디드 소프트웨어는 로그를 기록하기 위한 메모리, 파일시스템 공간이 제한적이기 때문에 크기 최소화
-> 데이터 전체를 로그로 남기는 대신 핑거 프린트를 사용하여 데이터 변경 여부만 파악
기능적인 측면에 초점을 두고 통합 테스트
- 컴포넌트 테스트를 수행할 때는 스텁을 사용했지만, 통합 테스트에서는 실제 컴포넌트를 사용
- 컴포넌트 간 연결 자체에서 오작동이 발생할 가능성이 높다면 상호작용에만 초점을 두고 테스트
- 컴포넌트 간 통신에 문제가 있을 가능성이 작다면 기능 위주 통합 테스트 수행
점진적 통합
- 통합 대상 컴포넌트가 많아서 한 번에 통합하여 테스트하는 방법을 빅뱅 방식이라고 함
- 한 번에 통합하는 경우 이 중에 결함을 가진 컴포넌트를 찾는 것은 쉽지 않음
- 이 때 빅뱅 방식 대신 점진적 방식을 적용하는 것이 효과적
- 점진적 방식은 오작동의 원인이 되는 컴포넌트를 찾기 쉽지만 테스트 드라이버, 스텁을 여러 번 개발해야 함
통합 순서 | 상향식 통합 | 하향식 통합 |
1차 | 컴포넌트 1 컴포넌트 2 |
컴포넌트 0 컴포넌트 1 |
2차 | 컴포넌트 0 컴포넌트 1 |
컴포넌트 1 컴포넌트 2 |
상향식 통합
- 하위의 컴포넌트를 식별하여 그룹화, 클러스터링한 후에 테스트 드라이버를 작성하여 테스트를 수행
- 여러 모듈의 묶음을 클러스터 또는 빌드라고 함
- 클러스터를 테스트 한 후 드라이버를 제거하고 실제 모듈과 결합
- 이 과정을 시스템이 완전히 통합될 때까지 반복
- 하위 컴포넌트를 충분하게 테스트할 수 있음
하위의 컴포넌트는 시스템이 제공하는 서비스에 필요한 공통적인 기능을 제공하는 역할을 함.
컴포넌트가 하위에 있을수록 여러 상위 컴포넌트가 빈번하게 사용하는 코드를 갖는다고 간주, 빈번하게 테스트 - 하향식 통합에서 필요한 테스트 스텁을 제공하는 비용이 들지 않음
하향식 통합
- 가장 상위에 있는 컴포넌트 테스트를 위해 하위 컴포넌트를 테스트 스텁으로 대치하고 테스트를 수행
- BFS나 DFS를 사용하여 테스트 스텁을 한 번에 하나씩 실제 컴포넌트로 대치하고
대치된 컴포넌트가 실제 호출하는 하위 컴포넌트를 테스트 스텁으로 대치 - 테스트 스텁이 실제 모듈로 대치되어 시스템에 변경이 발생해서 리그레션 테스트를 수행
- 컴포넌트 종속 관계에서 기본적으로 상위는 시스템의 기능을 결정, 하위는 시스템이 제공하는 기능을 보조
- 하향식 통합 테스트 방법은 많은 수의 테스트 스텁이 필요
샌드위치 통합
- 상위 컴포넌트 M1은 M2, M3, M4에 해당하는 테스트 스텁을 사용하여 테스트하고,
M5, M6, M7은 클러스터링하여 테스트 드라이버 D1을 사용하여 테스트 - 클러스터에 대한 테스트가 완료되면 실제 컴포넌트 M2를 통합하고
나머지 컴포넌트 M3,M4도 추가 통합하여 완전한 시스템 구축
시스템 테스트 및 인수 테스트
시스템 테스트
- 통합 테스트 완료 후 전체 시스템이 시스템 명세에 따라 개발되었는지 검증하기 위해 수행하는 테스트
- 기능 측면뿐만 아니라 성능, 호환성, 사용성, 신뢰성, 보안성, 유지보수성, 이식성 등과 같은 비기능적인 요구사항을 만족하는지도 검증
- 시스템 테스트가 완료되면 실제 사용자의 요구사항을 만족하는지 확인하기 위한 인수 테스트를 수행해야 함
인수 테스트
- 시스템 테스트 완료 후 실제 사용자의 요구사항을 만족하는지 확인하기 위해 인수 테스트 수행
- 인수 테스트 주목적은 결함 검출이 아니라 시스템을 인수해도 되는지 고객의 입장에서 평가
- 테스트 케이스는 사용자 또는 소프트웨어 구입자가 제시할 수도 있고,
시스템 테스트에서 사용했던 테스트 케이스를 사용할 수도 있음 - 알파 테스트, 베타 테스트가 있음
알파 테스트 : 선택된 사용자가 개발자 환경에서 통제된 상태로 수행
베타 테스트 : 일정 수의 사용자에게 소프트웨어를 사용하게 하고 피드백을 받음(보통 개발자가 참여하지 않음)
리그레션 테스트
유지보수 단계에서도 소프트웨어가 수정된 후 변경이 올바르게 되었는지 검사하기위해 리그레션 테스트 수행
유지보수 단계에서의 소프트웨어 수정
- 결함 수정 작업 : 소프트웨어를 사용하는 중에 발견된 결함을 수정하기 위해 소프트웨어를 변경하는 유지보수 활동
- 기능 보강 작업 : 소프트웨어 기능을 추가하거나 성능을 개선하기 위해 소프트웨어를 변경하는 유지보수 활동
- 적응 작업 : 소프트웨어 시스템을 새로운 운영환경에 적응시키기 위해 소프트웨어를 변경하는 유지보수 활동
- 예방 작업 : 더 나은 유지보수를 위해 기존 소프트웨어 시스템에 대한 문서 준비, 시스템 구조를 유지보수하기 용이한 새로운 구조로 변경하는 작업
유지 보수 활동에는 필연적으로 소프트웨어 수정 작업이 수반됨
소프트웨어가 변경되었을 때 변경 작업으로 인해 새로운 결함이 유발되었는지 살펴보아야 함
보통 유지보수 단계에서 리그레션 테스트를 수행하는 방법은 개발 단계에서 사용한 테스트 케이스를 이용
의도한 변화가 아니라면 프로그램에 결함이 있다고 판단
리그레션 테스트에서 중요하게 고려할 점은 테스트 케이스의 규모
시간이 갈수록 새로운 기능이 추가되는 시스템이라면 리그레션 테스트에 소요되는 비용과 시간도 늘어날 것
리그레션 테스트에도 여러 방식이 있음
- Retest-All : 기존 개발된 모든 테스트 케이스를 사용하는 방식
복잡한 테스트 절차를 요구하지 않지만 너무 많은 시간과 자원 필요 - 선택적 리그레션 테스트 : 기존 테스트 케이스 중 일부만 선정하는 방식
슬라이싱 기법, 자료 흐름 분석 기법같은 변경 영향 분석을 통해 다른 결과를 출력하는 테스트 케이스 식별 - 테스트 최소화 : 중복된 테스트 케이스를 제거해서 테스트 케이스 수를 줄이는 방식, 커버리지 방식 사용
영향을 받는 부분을 테스트 할 테스트 케이스가 제거될 위험이 있음 - 테스트 우선 순위화 : 테스트 케이스에 우선순위를 두고 우선 순위 높은 테스트 케이스만 활용
가능한 한 빨리 결함을 검출할 수 있도록 테스트 케이스의 실행 순서 결정
APFD가 많이 사용됨
테스트 케이스 실행 순서에 따라 APFD가 큰 차이를 보임
APFD가 높다는 것은 더 적은 수의 테스트 케이스를 실행하여 많은 결함을 빠르게 검출할 수 있음을 의미
결함 검출률이 높은 테스트 케이스 식별은 현실적으로 어려움
비즈니스 중요도, 리스크, 테스트 케이스의 실행 시간, 커버리지, 결함 검출 내역 등의 요인을 고려하여 우선순위 결정
리그레션 테스트는 컴포넌트 테스트, 통합 테스트, 시스템 테스트를 비롯한 모든 단계에서 수행
- 테스트 유지보수 단계에는 더 이상 필요 없는 테스트 케이스를 제거하는 과정이 있음
- 더 이상 비즈니스에 필요하지 않아 애플리케이션에서 제거되면
이를 테스트하는 테스트 케이스도 리그레션 테스트 케이스 집합에서 제거되어야 함 - 요구사항이 변경되어 입력 출력의 관계가 변할 수 있음
- 해당 테스트 케이스를 요구사항 변경에 따라 변경하는 작업이 필요
- 새로 추가된 테스트 케이스가 있으면 이 테스트 케이스를 자동화하는 편이 효율적
- 새로 추가된 테스트 케이스를 자동화하는 작업도 중요한 테스트 유지보수 작업에 해당