Introduction

소프트웨어 테스트는 결함을 발견할 의도로 프로그램을 실행시키거나 검사하는 활동입니다. 과거에는 프로그램이 잘 돌아가는지 확인하는 수준에 그쳤지만, 현대의 테스트는 아직 발생하지 않은 결함을 사전에 찾아내는 것을 목표로 합니다. 이 글에서는 정적·동적 테스트의 구분, Verification과 Validation의 차이, 테스팅 전략의 소용돌이 모형, 그리고 단위 테스트부터 인수 테스트까지의 테스팅 단계를 살펴봅니다.

테스트

Definition
  • the process of executing program or system with intent of finding errors. [The Art of Software Testing, 1979]
  • set of activities conducted to facilitate discovery and evaluation of properties of test items. [ISO/IEC/IEEE 29119-1:2022]

테스트란, 결함을 발견할 의도로 프로그램이나 시스템을 실행시키는 것 혹은 테스트 대상의 특성을 발견하고 평가하기 위해 수행되는 일련의 활동을 가리킨다. 과거의 테스트는 그저 프로그램을 실행시키고 그 프로그램이 잘 실행되는지 확인하는 것이 전부였지만, 이제는 아직 발생하지 않은 결함을 찾기 위해 테스트를 진행한다. 그 방법은 정적static 테스트와 동적dynamic 테스트로 나뉘는데, 두 가지 테스트를 가르는 기준은 프로그램의 실행 여부다.

테스트를 수행하는 관점도 크게 두 가지로 나뉘는데, 바로 확인Verification과 검증Validation이다. 한국어로는 둘 다 "검증"으로 번역되기 쉽지만, 소프트웨어 테스팅의 영역에서 이 둘의 의미는 명백히 구분된다. Verification은 "제품이 명세대로 만들어졌는가"를, Validation은 "올바른 제품이 만들어졌는가"를 판단한다. 명세서가 사용자의 실제 요구를 완벽하게 반영한다면 Verification만으로도 충분하겠지만, 현실에서는 요구사항을 전부 정확히 파악하기 어렵기 때문에 두 관점이 모두 필요하다.

정적 테스트

정적 테스트는 프로그램을 실행하지 않고 테스트를 진행하는 것이다. 사람이 직접 수행하는 방법과 도구를 활용하는 방법으로 나뉜다.

사람이 수행하는 정적 테스트의 대표적인 형태는 기술 리뷰technical review다. 기술 리뷰에는 비공식적으로 동료가 코드를 살펴보는 워크스루walkthrough부터 정해진 절차와 역할에 따라 체계적으로 결함을 찾는 인스펙션inspection까지 다양한 수준이 있다. Pressman은 효과적인 테스트를 위해서는 테스트를 시작하기 전에 기술 리뷰를 수행하여 많은 오류를 사전에 제거해야 한다고 강조한다.1

사람이 수행하는 정적 테스트 중 가장 체계적인 형태는 Fagan이 IBM에서 정립한 공식 인스펙션formal inspection이다 (Fagan, 1976)2. Fagan 인스펙션은 계획Planning → 개관Overview → 준비Preparation → 인스펙션 회의Inspection Meeting → 재작업Rework → 후속 조치Follow-up의 여섯 단계로 구성되며, 각 참가자에게 중재자Moderator, 작성자Author, 검토자Reviewer, 기록자Reader 등의 명확한 역할이 부여된다. Fagan의 원래 연구에 따르면, 설계와 코드 인스펙션은 테스트 이전에 결함의 60~90%를 제거할 수 있었으며, 이 결과는 이후 수많은 산업 현장에서 재현되었다. Pressman은 이를 "테스트 전 필터"라고 표현한다 (Pressman & Maxim, 2020, Ch.16).

도구를 활용하는 정적 테스트로는 정적 분석Static Analysis이 있다. 정적 분석 도구는 소스 코드를 실행하지 않은 채로 코드의 구조를 분석하여 잠재적 결함, 코딩 규칙 위반, 보안 취약점 등을 자동으로 검출한다. 컴파일러가 수행하는 타입 검사type checking도 넓은 의미에서 정적 분석에 해당한다.

동적 테스트

동적 테스트는 프로그램을 실제로 실행하면서 결함을 찾는 방법이다. 테스트 케이스를 설계하는 관점에 따라 크게 화이트박스 테스트white-box testing와 블랙박스 테스트black-box testing로 나뉜다.

화이트박스 테스트는 프로그램의 내부 구조, 즉 제어 흐름이나 데이터 흐름을 알고 있는 상태에서 특정 경로를 의도적으로 실행시키는 방식이다. 구문 커버리지statement coverage, 분기 커버리지branch coverage, 경로 커버리지path coverage 등 다양한 커버리지 기준이 이 범주에 속한다. 반면 블랙박스 테스트는 내부 구조를 모른 채 입력과 출력의 관계만으로 테스트 케이스를 설계한다. 동치 분할equivalence partitioning, 경곗값 분석boundary value analysis 등이 대표적인 기법이다.

테스트 크기 분류: Google의 접근

전통적인 화이트박스/블랙박스 구분과 별개로, 대규모 소프트웨어 조직에서는 테스트의 범위보다 크기로 분류하는 접근이 실용적으로 더 유효하다는 인식이 확산되고 있다. Google은 테스트를 소형Small, 중형Medium, 대형Large의 세 가지 크기로 분류한다 (Winters et al., 2020, Ch.11)3.

소형 테스트는 단일 프로세스 내에서 실행되며, 네트워크 호출이나 디스크 I/O, sleep 같은 블로킹 호출이 금지된다. 밀리초 단위로 실행되므로 개발자의 즉각적인 피드백 루프를 보장한다. 중형 테스트는 단일 머신 내에서 여러 프로세스를 사용할 수 있으며, localhost에 대한 네트워크 호출이 허용된다. 대형 테스트는 여러 머신에 걸쳐 실행될 수 있으며, 외부 서비스와의 통합을 검증한다.

이 분류의 핵심 가치는 각 크기별로 실행 환경의 제약 조건을 명시적으로 정의함으로써 테스트의 결정론성determinism과 실행 속도를 보장한다는 점이다. Google은 전체 테스트의 약 80%를 소형 테스트로 작성하는 것을 권장하며, 이 비율이 테스트 스위트의 빠른 피드백과 높은 신뢰성을 동시에 달성하는 핵심이라고 설명한다.

Verification

Verification은 명세서대로 제품이 만들어졌는지를 테스트하는 관점이다. 그러니까 제품 자체를 평가하기보다는 요구사항에 맞게 제품이 제대로 만들어졌는지를 평가하는 것이다. Boehm의 표현을 빌리면, "Are we building the product right?"에 해당한다.

Validation

Validation은 알맞은 제품이 만들어졌는지를 테스트하는 관점이다. 즉 Verification과 달리 제품이 의도에 맞게 작동하는지를 평가하는 것이다. Boehm의 표현으로는 "Are we building the right product?"에 해당한다.4

Verification과 Validation은 테스트만으로 달성되는 것이 아니다. 기술 리뷰, 품질·형상 감사, 성능 모니터링, 시뮬레이션, 문서 리뷰, 사용성 테스트, 인수 테스트 등 다양한 SQA 활동이 V&V에 포함된다.

테스팅 전략

테스트 전략은 작은 단위에서 시작해 점차 범위를 넓혀가는 구조를 따른다. Pressman은 이를 소용돌이spiral 모형으로 설명한다. 소용돌이의 중심에서 단위 테스트가 시작되고, 바깥으로 나아가며 통합 테스트, 확인 테스트validation testing, 시스템 테스트 순으로 확장된다. 안쪽에서는 구현에 가까운 화이트박스 기법이 주로 쓰이고, 바깥으로 갈수록 요구사항과 사용자 시나리오에 기반한 블랙박스 기법의 비중이 커진다.

이 전략에는 몇 가지 공통 원칙이 있다. 테스트 전에 기술 리뷰를 통해 많은 오류를 미리 제거하는 것, 컴포넌트 수준에서 시작해 시스템 전체로 나아가는 것, 시점과 접근 방식에 따라 서로 다른 테스트 기법을 적용하는 것, 그리고 테스트와 디버깅debugging은 별개의 활동이지만 테스트 전략에 디버깅을 반드시 수용해야 한다는 것이다.

테스팅 단계

단위 테스트

단위 테스트는 유닛 테스트Unit Test 혹은 컴포넌트 테스트Component Test라고도 부르며, 소프트웨어의 가장 작은 설계 단위인 모듈이나 컴포넌트를 대상으로 하는 테스트다. 이 테스트는 주로 개발 당사자가 수행하며, 여러 컴포넌트를 동시에 병렬로 테스트할 수 있다.

단위 테스트에서는 컴포넌트의 내부 처리 로직과 데이터 구조가 주된 검증 대상이 된다. 구체적으로는 컴포넌트의 인터페이스interface를 통해 정보가 올바르게 들어오고 나가는지, 지역 데이터 구조local data structure가 무결성을 유지하는지, 경계 조건boundary condition이 제대로 처리되는지, 독립적인 실행 경로independent path가 모두 실행되는지, 그리고 오류 처리 경로error-handling path가 올바르게 동작하는지를 확인한다.

단위 테스트를 수행하려면 테스트 대상 컴포넌트를 독립적으로 실행할 수 있는 환경이 필요하다. 이를 위해 드라이버driver와 스텁stub이라는 보조 코드를 작성한다. 드라이버는 테스트 대상 컴포넌트를 호출하는 상위 프로그램의 역할을, 스텁은 테스트 대상이 호출하는 하위 컴포넌트를 대체하는 역할을 한다.5

통합 테스트

통합 테스트Integration Test는 단위 테스트를 통과한 컴포넌트들을 점진적으로 결합하면서 인터페이스 오류를 찾아내는 테스트다.6 개발 조직이 개발자의 관점에서 수행하며, 개발 환경 내지 통합 환경 하에서 주로 기능의 완성도나 인터페이스의 정상 작동 여부를 확인한다.

개별 컴포넌트가 모두 잘 동작한다고 해서 결합해도 문제가 없으리라는 보장은 없다. 인터페이스를 통해 데이터가 유실될 수 있고, 한 컴포넌트가 다른 컴포넌트에 예상치 못한 영향을 줄 수 있으며, 하위 기능이 합쳐졌을 때 기대한 상위 기능이 제대로 만들어지지 않을 수도 있다. 이 때문에 모든 컴포넌트를 한꺼번에 합친 뒤 테스트하는 빅뱅 접근법big bang approach은 오류의 원인을 분리하기 어려워 실패하기 쉽다.

그래서 통합 테스트는 점진적incremental으로 수행하는 것이 원칙이다. 대표적인 전략으로 하향식top-down과 상향식bottom-up이 있다. 하향식 통합은 최상위 제어 모듈부터 시작해 하위 모듈을 스텁으로 대체하면서 한 단계씩 실제 컴포넌트로 교체한다. 주요 제어 흐름과 의사결정 지점을 초기에 검증할 수 있다는 장점이 있다. 상향식 통합은 반대로 최하위 모듈부터 클러스터cluster로 묶어 테스트한 뒤 위로 올라가는 방식으로, 복잡한 스텁을 작성할 필요가 없다는 장점이 있다.

애자일 개발 환경에서는 지속적 통합Continuous Integration이 보편화되었다. 개발자가 코드를 커밋할 때마다 빌드와 테스트를 자동으로 수행하고, 스모크 테스트smoke test로 핵심 기능이 깨지지 않았는지를 매일 확인한다.7 통합 과정에서는 이전에 통과한 테스트를 다시 실행하는 회귀 테스트regression testing도 필수적이다. 새로 추가된 컴포넌트가 기존 기능에 부작용을 일으키지 않았는지를 확인하기 위함이다.

시스템 테스트

시스템 테스트System Test는 통합 작업이 모두 완료된 다음 실시하는 테스트다. 시스템을 구성하는 모든 컴포넌트가 시스템에 결합되고, 이 시스템이 사용자에게 배포되기 전에 개발 조직이 사용자의 관점에서 테스트를 수행한다. 테스트 환경도 실제 운영 환경 혹은 그에 준하는 환경으로 구성되며, 기능 검증뿐만 아니라 사용자가 체감하는 비기능적 품질 속성이 중요한 평가 기준으로 떠오른다.8

시스템 테스트에서 수행되는 비기능적 테스트로는 성능 테스트performance testing, 보안 테스트security testing, 스트레스 테스트stress testing 등이 있다. 성능 테스트는 동시 사용자 수, 단위 시간당 트랜잭션 수, 트랜잭션당 데이터 부하 등의 변수를 조합하여 시스템의 응답 시간과 처리량을 측정한다. 보안 테스트는 시스템에 내장된 보호 메커니즘이 부정한 침투로부터 시스템을 제대로 방어하는지 검증한다. 스트레스 테스트는 비정상적인 수준의 부하를 가하여 시스템이 어디까지 버틸 수 있는지 한계점을 파악한다. 이처럼 시스템 테스트는 시스템 전체가 요구사항대로 동작하는지 기능적·비기능적 양면에서 확인하는 단계다.

인수 테스트

시스템 테스트까지 통과하고 나면 테스트의 주체는 개발 조직에서 인수자, 즉 사용자로 넘어간다. 인수 테스트Acceptance Test는 시스템 인수 직전 사용자의 최종 점검이라고도 할 수 있으며, 실제 운영 환경 혹은 그에 준하는 환경에서 사용자가 고객의 관점으로 요구사항 충족 여부, 실사용 적합성, 법·규제 준수 여부 등을 확인한다.

인수 테스트는 수행 방식에 따라 알파 테스트alpha test와 베타 테스트beta test로 나뉜다. 알파 테스트는 개발자가 지켜보는 가운데 사용자가 통제된 환경에서 소프트웨어를 사용해 보는 것이고, 베타 테스트는 개발자가 참여하지 않는 상태에서 최종 사용자가 실제 환경에서 소프트웨어를 사용해 보는 것이다.9 이를 통해 고객은 시스템 인수 여부를 최종 결정한다.

테스트 자동화와 테스트 문화

테스트 전략이 아무리 정교해도 자동화 없이는 지속 가능하지 않다. 수동 테스트는 반복 실행의 비용이 높고, 회귀 테스트를 일관되게 수행하기 어렵기 때문이다. Pressman은 테스트 자동화가 단순히 도구의 문제가 아니라 테스트 프로세스의 설계 문제라고 강조한다 (Pressman & Maxim, 2020, Ch.22). 자동화할 테스트의 범위, 테스트 데이터 관리 전략, CI 파이프라인과의 통합 방식을 사전에 설계해야 자동화의 효과가 극대화된다.

Google의 경험은 이 점을 더욱 선명하게 보여준다. Winters et al. (2020, Ch.11)에 따르면, Google은 수십억 줄의 코드베이스에서 하루에 수백만 건의 테스트를 자동으로 실행하며, 이 테스트 인프라가 개발 속도를 높이는 핵심 요인으로 작용한다. 테스트 자동화의 진정한 가치는 "한 번 작성하면 무한히 반복 실행할 수 있다"는 점이며, 이는 코드베이스가 커질수록 그 가치가 기하급수적으로 증가한다10.

그러나 자동화는 도구만으로 달성되지 않는다. Jalote (2000)는 Infosys의 CMM 레벨 5 달성 과정에서 가장 어려웠던 부분이 기술적 인프라 구축이 아니라 엔지니어들의 품질 의식 전환이었다고 기술한다. 테스트를 "개발이 끝난 후에 하는 것"이 아니라 "개발의 일부"로 인식하는 문화적 전환이 선행되어야 자동화 도구의 투자가 실질적인 품질 향상으로 이어진다는 것이다. 이 관점은 SQA가 단순한 프로세스 준수를 넘어 조직 문화의 문제라는 전사적 품질 관리TQM의 원칙과도 맥이 닿는다.

마무리

테스트는 결함이 없음을 증명하는 것이 아니라 결함의 존재를 드러내는 활동이다. 정적 테스트와 동적 테스트로 결함을 찾고, Verification과 Validation으로 명세 부합성과 실사용 적합성을 각각 점검한다. 그리고 단위 테스트에서 인수 테스트까지 범위를 넓혀가며 개별 모듈의 정확성부터 시스템 전체의 사용자 경험까지 단계적으로 확인한다.

테스트 활동은 소프트웨어 품질 보증의 핵심 수단이며, 형상 관리를 통해 테스트 대상의 버전과 상태를 추적할 때 비로소 신뢰할 수 있는 결과를 얻을 수 있다.


출처

  • Myers, G.J., Sandler, C. and Badgett, T. (2011) The art of software testing. 3rd edition. Hoboken, NJ: John Wiley & Sons.
  • ISO/IEC/IEEE 29119-1:2022. Software and systems engineering — Software testing — Part 1: General concepts. International Organization for Standardization.
  • Pressman, R.S. and Maxim, B.R. (2020) Software engineering: a practitioner's approach. Ninth edition. New York, NY: McGraw-Hill Education. — Ch.16 Reviews, Ch.19 Component Level, Ch.20 Integration Level, Ch.21 Specialized Testing, Ch.22 Test Automation.
  • Boehm, B.W. (1981) Software engineering economics. Englewood Cliffs, NJ: Prentice Hall.
  • Winters, T., Manshreck, T. and Wright, H. (2020) Software engineering at Google: lessons learned from programming over time. Sebastopol, CA: O'Reilly Media. — Ch.9 Code Review, Ch.11 Testing Overview, Ch.12 Unit Testing.
  • Fagan, M.E. (1976) 'Design and Code Inspections to Reduce Errors in Program Development', IBM Systems Journal, 15(3), pp.182–211.
  • Jalote, P. (2000) CMM in practice: processes for executing software projects at Infosys. Reading, MA: Addison-Wesley.
  • McConnell, S. (1996) Rapid development: taming wild software schedules. Redmond, WA: Microsoft Press.

Footnotes

  1. 기술 리뷰는 테스트 이전의 필터 역할을 한다. Pressman에 따르면, 테스트 전략과 테스트 케이스 자체에 대해서도 기술 리뷰를 수행해야 한다.

  2. Fagan, M.E. (1976). IBM의 Kingston 연구소에서 OS/360 프로젝트를 대상으로 수행한 연구. 설계 인스펙션에서 결함의 82%를, 코드 인스펙션에서 추가로 결함의 상당수를 테스트 이전에 발견할 수 있었다. 이 방법론은 이후 IEEE 1028 표준의 기초가 되었다.

  3. Winters et al. (2020, Ch.11). Google은 테스트 크기를 기술적 제약 조건으로 정의하며, 테스트 범위scope(단위, 통합, 시스템)와는 직교orthogonal하는 개념으로 다룬다. 소형 단위 테스트가 가장 흔하지만, 소형 통합 테스트도 가능하다.

  4. Boehm, B. (1981) Software Engineering Economics. Prentice Hall. V&V에 대한 이 구분은 소프트웨어 공학에서 가장 널리 인용되는 정의 중 하나다.

  5. 드라이버는 테스트 데이터를 전달하고 결과를 출력하는 간단한 프로그램이며, 스텁은 호출되었을 때 미리 정해진 값을 반환하는 더미 컴포넌트다. 테스트가 끝나면 실제 컴포넌트로 교체된다.

  6. 그래서 사실 통합 테스트는 하나일 수 없다. 통합이 전혀 되지 않은 상태와 완전히 끝난 상태 사이에는 수많은 통합 과정이 포함되어 있기 때문에, 통합 테스트는 각 컴포넌트를 하나로 결합하는 과정에서 여러 가지 형태로 존재한다.

  7. 스모크 테스트는 빌드 전체를 끝에서 끝까지end to end 실행하여 치명적인 오류가 없는지 확인하는 빠른 검증이다. McConnell은 "스모크 테스트를 통과하면 더 정밀한 테스트를 진행할 만큼 빌드가 안정적이라고 가정할 수 있다"고 설명한다.

  8. 그렇다고 해서 앞선 단계에서 비기능적 품질 속성을 테스트하지 않았다는 뜻은 아니다. 다만 사용자가 체감하는 비기능적 품질 속성은 시스템 구성이 완료되고 나서야 제대로 테스트할 수 있다는 의미다.

  9. 알파 테스트는 개발 현장에서 이루어지며 개발자가 오류를 즉시 기록할 수 있다. 베타 테스트는 사용자의 실제 환경에서 이루어지며, 사용자가 발견한 문제를 개발자에게 보고하는 방식으로 진행된다.

  10. Winters et al. (2020, Ch.12)은 이를 "복리 효과compound interest"에 비유한다. 테스트 작성은 초기 비용이지만, 이후 코드가 변경될 때마다 자동으로 회귀 검증을 수행하므로 시간이 갈수록 투자 대비 수익이 증가한다.