QA for Beginners
앞서 다음과 같은 순서로 QA 프로세스가 진행된다고 얘기했었다.
QA Plan > Entry Criteria > QA Start > Test Execute > Exit Criteria > QA Sign off
* Test Execute: Testing > Bug Report > Issue Tracking
이 중 앞서 다루지 않았던 Test Execute(테스트 수행)에 대해 조금 더 구체적으로 말해보자면, 개괄적인 시각에서 테스트는 테스트를 하고, 버그(결함)를 찾아, 유관 부서에 이슈를 공유하고 수정 여부를 확인하는 과정을 반복하는 활동으로 볼 수 있다. 이때 테스팅에는 여러 가지 방법들을 적용할 수 있는데, F/E QA 프로젝트에 TE로 참여했을 때 여러 회사들에게서 대체로
Sanity Test > TC Run > Regression Test / Experience-based Test / AD-HOC Test
와 유사한 순서로 테스트가 수행된다는 공통점이 있었다. 따라서 이번에는 이 4개로 테스트 기법을 한정하여, 각각 어떤 테스트를 수행하게 되는지, 테스트에는 TC 이외에 어떤 문서들을 활용할 수 있는지 ISTQB Syllabus의 정의보다는 쉽게 풀어서 이야기해 보겠다.
Sanity Test는 신규 빌드가 테스트 수행할 수 있는 수준인지를 판단하기 위해 수행하는 테스트이다. 인스턴트커피 스틱으로 커피가 잘 녹는지를 확인하려고 커피 스틱을 뜯는데 맨손으론 개봉할 수 없고, 주변에 마땅한 도구도 없다면 커피의 용해성을 테스트하긴커녕 당장 스틱의 사용 용이성부터 개선해야 할 것이다. Sanity Test는 바로 스틱을 개봉할 수 있는지를 점검하는 테스트라고 할 수 있다.
Sanity Test는 Smoke Test와 혼동되곤 한다. 직역하자면 무결성 테스트와 연기 테스트인데, 결함이 없는 것과 테스트하기에 문제가 없는 것이 말장난같이 보이기 때문이다. 이해하기 쉽게 정리하자면,
Smoke Test
방금 완성한 프로토타입의 히터를 켰을 때 연기가 나지 않는지를 테스트한다. 소프트웨어의 핵심 기능들이 기대한 대로 동작하는지를 검증하며, ’방금 완성한‘ 이라는 특성에 맞게 이를 점검하는 사람은 개발자 혹은 테스터로 구성된다. 세세하게 테스트하진 않지만 되도록 전체 기능을 커버할 수 있도록 TC를 구성하여 테스트한다.
Sanity Test
시제품 히터를 수령하여 버튼을 누르면 히터가 켜지는지를 테스트한다. 이전과 다른 버튼으로 교체한 이후에는 전원이 기대한 대로 동작하는지를 검증하며, ‘수령한’이라는 특성에 맞게 이를 점검하는 사람은 주로 테스터가 된다. 전체 기능을 테스트하진 않지만 일부 기능으로 범위를 한정하여, 변경된 기능이 온도 체크, 온도 변경, 시간 예약 등 준비된 TC를 기반으로 아주 구체적인 테스트로 진입하기 적합한지를 테스트한다.
Regression Test는 말 그대로 반복 테스트로, Side Effect나 이전의 결함으로 인해 발견되지 않았던 또 다른 결함을 발견하기 위해 수행한다. 예를 들어 테스트 앱에서 API 호출 시 로그를 찍어주는데, 400 에러가 발생하는 이슈가 이제 막 수정되었다고 하자. 이 경우 이슈 수정 여부를 확인하기 위해 이미 테스트했던 부분을 다시 테스트해야 할 것이다. 또는 해당 이슈가 API 전반에 걸쳐 무작위로 재현된다면, API 관련 기능을 모두 재검증해 보는 것이 좋을 것이다. 이렇게 테스트했던 프로그램의 테스팅을 반복하는 것이 Regression Test이다.
그런데 만약 200으로 API 호출이 성공하긴 하는데, 로그가 2초마다 반복해서 찍히는 새로운 결함이 발견되었다고 하자. 이렇게 결함을 수정하면서 또 다른 결함이 발생하는 것을 Side Effect라고 한다.
Experience-based Test, 즉 경험 기반 테스트는 테스터의 경험에 의존하는 테스트이다. 이전에 다룬 적 있는 기술이나 프로덕트에서의 경험, 직관, 개인 역량 등을 활용한다. 테스트 대상 소프트웨어에 대한 지식이 필연적으로 필요하기에, 경험이 많은 테스터일수록 창의적인 테스트가 가능하다는 특징이 있다.
다른 프로젝트에서 파일 다운로드 시 영어 외의 문자는 모두 깨져서 노출되던 인코딩 이슈가 있었는데, 이번에 참여한 프로젝트에서 한글 닉네임을 등록 시 프로필 영역에서 글자가 깨지는 결함을 발견했다면 결함의 원인을 인코딩 방식으로 의심할 수 있을 것이다. 이런 경우가 Experience-based Test에서 수행하는 방식이다. 따라서 보통은 TC 수행을 완료한 뒤 추가 검증으로써 수행하게 된다. Exploratory Test(탐색적 테스트), Error Guessing(오류 추정), Checklist 등 여러 종류가 있는데, 이들의 목적은 모두 명세 기반 등을 이용한 공식적인 테스트에서 다루기는 어렵지만 Risk가 높을 것으로 예상되는 부분을 커버하려는 하나의 목적으로 귀결된다.
AD-HOC Test를 직역하면 즉석 테스트로 번역할 수 있다. 이름에서 추론할 수 있듯, AD-HOC Test는 자의적이고 임의적으로 실행하는 테스트 활동을 의미한다. 이와 비슷하게 Random Test가 있는데,
Random Test는 테스트를 설계할 때 의도적으로 입력값을 정의해주지 않아, 임의로 값을 선택하여 수행하도록 한다면
AD-HOC Test는 우리가 웹브라우저로 Youtube를 볼 때 ‘난 오직 Chrome 브라우저로 딱 한 개의 탭만 열어서 Youtube를 볼 거야’라고 계획하지 않는 것처럼, 테스트 설계 자체를 하지 않고 수행된다.
공인된 설계 기법을 적용하거나, 예상 결과를 사전에 정의하지 않는 비공식 테스트인 것이다. 따라서 Experience-based Test와 마찬가지로 TC 수행을 완료한 뒤 추가 검증을 위해 수행하거나, 프로젝트의 규모가 작거나, TC를 설계하여 문서화할 시간조차 없을 때 곧바로 수행하기도 한다.
의도적으로 이렇게 하는 이유는 두 가지가 있는데, 첫째로 예상 결과를 미리 정의하지 않으면, 발견되는 결함 또한 예상외의 결과를 가져다주기 때문이다. 이 결함은 Trivial 한 결함일 수도 있고, 지금까지 발견된 이슈 중 가장 Critical 한 결함일 수도 있다. 둘째는 예상 결과를 정의함으로써 만들어지는 틀에서 벗어난 테스트가 가능하기 때문이다.
그렇다면 애초부터 AD-HOC Test를 하면 되는 것 아닌가? TC로 명세화하고 이를 기반으로 테스트하는 의미가 무엇인지 궁금할 것이다.
TC를 쓰는 이유는 아직 발굴되지 않은 유적이 어딘가에 있을지라도, 우리 집 지하 2층 주차장범위만큼은 존재하지 않음을 보장할 수 있듯이 적어도 명세한 내용에 대해서는 검증을 완료했다는 근거로 삼기 위해서이다. 근거로 삼을 산출물 없이 테스트를 수행한다면 당장은 문제없이 Sign off를 할 수 있을지라도, 이후에 발생한 결함이 현재 빌드의 결함을 수정함으로써 발생한 Side Effect인지 혹은 세 달 전에 테스트했을 때 검증 영역에서 누락되어서 발생한 잠재 결함이었는지를 판단할 수 없을 것이다.
지금까지 TC에 대해서만 언급했지만, 문서화의 방법 중에는 Checklist나 Test Scenario도 있다. 이 셋의 성격은 명백히 다른데, ISTQB의 설명이 다소 추상적이므로, 알고리즘이란 무엇인지를 설명할 때 흔하게 예로 드는 라면 끓이기로 차이를 이야기해 보겠다.
Test Case는 테스트 대상을 작은 단위로 쪼개어, 각각의 케이스에 대한 사전 조건(Precondition)과 확인 과정(Test Steps), 기대 결과(Expected Result)와 실제 결과(Test Result) 등을 구체적으로 기술한다. 즉, 특정 상황일 때 어떤 결과가 발생하는지에 대해 확인하는 행위이다. 처음 투입된 인원도 TC를 보고 문제없이 테스트를 수행할 수 있다면 잘 쓴 TC라고 할 수 있다. 상기 4개가 가장 보편적으로 정의를 하며, 필요에 따라 정의 내용을 가감하기도 한다. 라면 끓이는 법을 TC로 기술한다면 아래와 같이 자세하게 기술할 수 있다.
Precondition: 라면 1 봉지, 인덕션에 올린 상태인 500ml의 물이 담긴 냄비
Test Steps / Expected Result / Test Result
라면 봉지 뜯기 / 라면 봉지가 개봉됨 / Pass or Failed or …
라면 봉지를 뜯어 봉지 안의 구성품 확인 / 라면과 스프 봉지가 포함됨 / Pass or Failed or …
인덕션을 켜서 100도까지 냄비를 가열 / 물이 팔팔 끓음을 확인 / Pass or Failed or …
끓는 물에 라면과 스프 모두 넣기 / 라면과 스프가 담긴 냄비가 계속 끓음을 확인/ Pass or Failed or …
반면 Checklist는 TC보다 간소화하여 작성한다. 이때 처음 투입된 인원일 경우 생략된 부분이 무엇인지, 기대 결과를 어떤 과정을 통해 검증할 수 있는지를 이해하기는 어렵다. 따라서 Checklist를 작성할 때는 테스터가 어느 정도 히스토리를 알고 있다고 가정하고 작성한다.
Precondition: 라면 1 봉지, 인덕션에 올린 상태인 500ml의 물이 담긴 냄비
Test Steps / Test Result
라면과 스프 준비 가능한지 확인 / Pass or Failed or …
인덕션에서 물이 끓는지 확인 / Pass or Failed or …
라면이 끓는지 확인 / Pass or Failed or …
Checklist가 마트에서 구매할 목록을 확인한다면, TC는 마트에 갈 수 있는지부터 확인한다. 즉, TC가 다소 ‘이렇게까지..?‘ 싶을 정도로 동작을 쪼개어 자세하게 기술한다면, Checklist는 어느 정도 추상화하여 Test Steps를 기술한다.
Test Scenario는 테스트 과정을 구체적으로 기술하는 데에 초점이 맞춰져 있는데, TC들을 일련의 순서로 조합한다고 생각하면 쉽게 이해할 수 있다. 위의 예시를 Test Scenario로 작성한다면 다음과 같이 작성할 수 있다.
다음과 같은 5개의 TC가 작성되어 있다고 할 때,
#1: 물이 있는지 확인
#2: 인덕션이 있는지 확인
#3: 라면이 있는지 확인
#4: 라면 스프가 있는지 확인
#5: 3분 30초동안 끓이는지 확인 (Precondition: 라면을 끓일 환경이 구성된 상태)
Description / TC No. / Test Result
라면을 끓일 수 없는 경우 / #1, #2, #3, #4 / Pass or Failed or …
우유 라면을 끓이는 경우 / #1, #5 / Pass or Failed or …
전자레인지로 라면을 끓이는 경우 / #2, #5 / Pass or Failed or …
Checklist와 유사해 보이지만, Test Scenario는 마트에 ’가서‘ 구매할 목록을 ‘찾아서 구매‘하는 과정을 확인한다. 흐름의 관점에서 기술하기 때문에 개별 흐름들은 가는 방법, 구매할 목록을 찾는 순서, 최종 구매하는 상품 등을 조합한 결과가 정의된다.
테스트 기법들은 그 정의와 차이점을 명확하게 이해하여 적재적소에 적용할 수 있어야 한다. 무작정 최대한 많은 기법들을 적용한다고 해서 좋은 것만은 아니기 때문이다. Checklist와 Test Scenario를 같이 활용하는 바람에 이미 검증한 영역을 중복 검증할 수도 있고, 이로 인해 불필요한 리소스가 소요되는 바람에 기존 계획보다 더 제한적인 부분만을 검증하게 될 수도 있다. 또는 테스터의 창의성에 의존하여 Experience-based Test만 수행한 덕분에 리소스는 효율적으로 활용할 수 있었지만, 직전 빌드에서 검증한 부분을 빠뜨리는 등 일관되지 못한 테스트를 수행하게 될 수 있다.
리소스라는 왼쪽 날개와 테스트 기법이라는 오른쪽 날개의
균형을 맞출 때 릴리스라는 목표를 달성할 수 있다.
적재적소에 테스트 기법을 적용할 줄 아는 QA로 성장하길 바란다.