테스트는 소프트웨어 엔지니어가 해야 하는 중요한 작업입니다. 테스팅과 관련된 질문들은 보통 다음 네 가지 범주 중 하나에 속한다.
- 실생활에서 만나는 객체(펜과 같은)를 테스트하라.
- 소프트웨어를 하나 테스트하라.
- 주어진 함수에 대한 테스트 코드를 작성하라.
- 발생한 이슈에 대한 해결책을 찾아내라.
이 네 가지 범주 각각에 대한 접근법을 지금부터 살펴보겠습니다. 잘 정돈된 입력이 주어지거나, 사용자가 지정된 가이드라인을 따라 시스템을 사용할 것이라는 가정을 하면 안된다 는 사실을 기억하시기 바랍니다. 그릇된 방식으로 시스템이 사용될 수 있으니, 그에 대비해야 합니다.
면접관이 찾는 것은
표면적으로만 보면 테스트에 관계된 문제들은 광범위한 테스트 케이스 목록을 만들어 내는 것에 관계된 것처럼 보입니다. 어느 정도는 맞는 말이다. 적절한 테스트 케이스들을 만들어 내야 합니다. 하지만 면접관들이 보고자 하는 것은 더 있습니다.
-
큰 그림을 이해하고 있는가: 당신은 소프트웨어가 지향하는 바가 무엇인지 정말로 이해하고 있는 사람인가? 테스트 케이스 간의 우선순위 를 적절히 매길 수 있는가? 가령 여러분이 아마존과 같은 전자 상거래 시스템을 테스트 하라는 요구를 받았다 해보자. 상품 이미지가 적절한 장소에 정확하게 뜨는지 확인하는 것은 아주 중요합니다. 하지만 구매 대금이 정확하게 지불되도록 하는 것, 배송 목록이 정확히 갱신되도록 하는 것, 그리고 대금이 이중으로 청구되도록 하지 않는 것이 훨씬 더 중요합니다.
-
퍼즐 조각을 제대로 맞추는 방법을 아는가: 소프트웨어가 어떻게 동작하는지, 그리고 각 소프트웨어가 보다 더 큰 생태계의 일부로 어떻게 귀속되는지 이해하고 있는가? 여러분이 구글의 스프레드시트 소프트웨어를 테스트하라는 업무를 할당받았다 해보자. 하지만 구글 스프레드시트는 더 큰 생태계의 일부분이다. Gmail이나 플러그-인 등의 다른 컴포넌트들과 제대로 통합되는지 테스트 해봐야 한다.
-
조직화: 문제에 구조적으로 접근하고 있는가, 아니면 생각나는 대로 아무 방법이나 질러보고 있는가? 어떤 응시자는 카메라에 대한 테스트 케이스를 찾으라는 문제를 던지면 떠오르는 대로 아무 테스트나 만들어 낸다. 훌륭한 응시자는 ‘사진 촬영’, ‘이미지 관리’, ‘설정’ 등의 범주로 시스템을 나눈 다음에 테스트를 만들어 간다. 이런 구조적 접근법을 사용하면 테스트 케이스를 보다 풍부하게 찾아 나갈 수 있다.
-
실용성: 실제로 적용하기 적절한 테스트 계획을 만들 수 있나? 가령 어떤 사용자가 특정한 이미지를 여는 순간 소프트웨어가 다운된다고 보고 있다고 하자. 여러분이 그 사용자에게 소프트웨어를 재설치하라고 말한다면, 그건 그다지 실용적인 답이 못 된다. 여러분이 만든 테스트 계획은 실행 가능해야 하고, 실제로 구현할 수 있는 실용적인 것이 되어야 한다.
이러한 측면들을 고려하고 있다는 사실을 드러내 보이면 테스팅 팀의 중요한 일원이 될 수 있다는 사실을 보일 수 있습니다.
실제 세계의 객체 테스트하기
어떤 지원자는 ‘펜을 테스트하라’와 같은 질문을 받으면 당황합니다.
질문: 클립을 테스트하려면 어떻게 하겠습니까?
- 사용자는 누구인가? 왜인가?
면접관과 어떤 사용자가 어떤 목적으로 제품을 사용하는지 의논해봐야 한다. 생각헀던 것과는 다른 답이 나올 수 있다. 가령 “선생님들이, 신문을 함께 철해 두기 위해 사용한다”는 답을 들을 수도 있고, “조각가가 동물 모양으로 구부리기 위해 사용한다”는 답을 들을 수도 있다. 둘 다일 수도 있다. 면접관이 어떤 답을 하느냐에 따라 남은 질문들을 어떻게 처리해야 할지 생각해야 합니다.
- 어떤 유스케이스(use case)가 있나?
유스케이스의 목록을 만들어 두면 도움될 것이다. 이 문제의 경우, 유스케이스는 단순히 ‘신문을 망가뜨리지 않고 함께 철한다’가 될 것이다. 질문에 따라서는 유스케이스가 여러 개 도출될 수 있다. 뭔가를 보낼 수도 있고, 받을 수도 있고, 쓰거나 지울 수도 있을 것이다.
- 한계 조건은?
팅하나의 클립으로 철할 수 있는 신문은 몇 장인가? 몇 장 이상을 철하면 클립이 망가지는가? 한 번에 30장은 영구적 손상 없이 철할 수 있다거나, 30장에서 50장 정도는 약간의 손상으로 철할 수 있다거나 하는 정보를 찾아야 한다. 이 한계 조건은 환경적 요인들로 확대될 수 있다. 가령, 우리가 테스트하는 클립은 아주 더운 곳에서도 정상적으로 문서를 철할 수 있어야 한다거나, 하는 조건들이 그에 해당한다. 아주 추운 곳에서는 어떠한가?
- 스트레스/고장 조건은?
고장 나지 않는 제품은 없다. 따라서 고장이 발생하는 조건을 분석하는 것도 여러분이 해야 하는 일이다. 면접관과 토론해봐야 할 것 가운데 하나는 제품이 고장 나더라도 받아들일 수 있는 때는 (심지어는 필요한 때는) 언제인가 하는 것이고, 어떤 종류의 고장을 심각하게 간주해야 하느냐 하는 것이다. 가령, 여러분이 세탁기를 테스트한다면 여러분은 우선 해당 세탁기가 적어도 30벌의 셔츠나 바지를 세탁할 수 있는지 알아봐야 할 것이다. 30개에서 45개의 의류를 투입하면, 오염이 적절히 제거되지 않는 등의 사소한 고장이 발생할 수 있다. 45벌 이상의 옷을 넣으면 ‘극심한 고장’이 생기더라도 받아들일 수 있을 것이다. 물론 이 경우에 ‘극심한 고장’은 세탁기에 물이 공급되지 않는 등의 오작동이어야 한다. 물이 넘치거나, 화재가 발생한다거나 하는 것이 아니다.
- 테스트는 어떻게 수행할 것인가?
어떤 경우, 이것은 테스트 수행에 관계된 세부사항을 토론하는 것과 관계된 작업이다. 가령, 정상적으로 사용한다는 가정 하에 어떤 의자를 5년 동안은 문제 없이 사용할 수 있는지 테스트하고자 한다면, 그 의자를 실제로 집에 가져가서 오 년 동안 기다리고 싶지는 않을 것이다. 대신, ‘정상적으로 사용’한다는 것이 어떤 의미인지를 정의해야 한다(일년에 몇 번이나 앉으면 정상 사용인가? 팔걸이 부분은 어떤가?) 또한 수작업으로 테스트하는 것 이외에도, 기게가 수행하는 자동화된 테스트를 도입할 것도 고려해 봐야 한다.
소프트웨어 테스팅
하나의 소프트웨어를 테스트하는 것은 실제 세계의 객체를 테스트하는 것과 아주 유사합니다. 주된 차이점은 소프트웨어 테스팅의 경우 테스트 세부 사항을 더 많이 강조 한다는 것입니다. 소프트웨어 테스팅의 두 가지 핵심적 측면은 다음과 같습니다.
-
수동 테스트 vs. 자동화된 테스트: 실제 세계에서라면 모든 것을 자동화하고 싶겠지만, 항상 가능하지는 않다. 어떤 것들은 수동 테스트가 더 나은데, 컴퓨터가 효과적으로 검사할 수 있도록 정량화하기 어려운 정성적 특성이 너무 강하기 때문이다. 또한, 컴퓨터는 일반적으로 살펴보라 지시한 문제들만 인식하지만, 인간의 인지 능력은 특별히 검토된 적이 없는 새로운 문제들을 밝혀낼 수도 있다. 인간과 컴퓨터는 둘 다 테스트 프로세스의 핵심적 부분이다.
-
블랙 박스 테스트 vs. 화이트 박스 테스트: 이런 구분은 소프트웨어 내부를 어디까지 들여다 볼 수 있느냐에 근거한 것이다. 블랙 박스 테스트의 경우, 우리는 소프트웨어를 주어진 그대로 테스트 해야 한다. 화이트 박스 테스의 경우, 우리는 그 내부의 개별 함수들을 프로그램적으로 접근하여 테스트할 수 있다. 블랙 박스 테스트도 자동화할 수 있지만, 분명히 훨씬 더 어렵다.
소프트웨어 테스트에 적용할 수 있는 접근법 하나를 처음부터 끝까지 살펴보겠습니다.
- 블랙 박스 테스트를 하고 있는가 아니면 화이트 박스 테스트를 하고 있는가?
테스트의 마지막 단계에 가서야 이런 질문을 던지는 경우도 많지만, 가능한 한 일찍 이런 질문을 던지는 쪽이 좋습니다. 면접관에게 블랙 박스 테스트를 해야 하는 것인지 아니면 화이트 박스 테스트를 해야 하는지, 아니면 둘 다 해야 하는지 확인하라.
- 누가 사용할 것인가? 왜 사용하는가?
소프트웨어는 보통 한 부류 이상의 사용자를 대상으로 한다. 소프트웨어의 기능은 그 점을 염두에 두고 설계된다. 가령 여러분이 부모가 웹 브라우저를 통제할 수 있도록 하는 소프트웨어를 테스트하라는 주문을 받으면, 여러분이 고려해야 할 사용자는 부모(차단을 실행하는 사용자)와 아이들(차단 대상 사용자)이다. 차단 대상도 아니고 차단 실행자도 아닌 ‘손님Guest’도 사용자 중 하나가 될 수 있다.
- 어떤 유스케이스들이 있나?
앞서 살펴본 차단 소프트웨어의 경우, 부모의 유스케이스는 소프트웨어를 설치하고, 차단 기능을 활성화하고, 차단을 해제하고, 인터넷을 사용하는 등의 행위들로 구성된다. 아이들의 경우에는 불법적인 콘텐츠에 접근하는 경우와 합법적 콘텐츠에 접근하는 경우로 나누어 볼 수 있을 것이다.
- 사용 한계(Bounds of use) 지점은?
유스케이스가 모호하므로, 그 의미를 정확히 알아 내야 한다. 웹사이트를 차단한다는 것은 어떤 의미인가?불법 콘텐츠를 담고 있는 특정한 페이지만 차단한다는 의미인가, 아니면 그 페이지를 포함하는 전체 웹사이트를 차단한다는 의미여야 한다는 뜻인가? 무엇이 나쁜 콘텐츠인지 프로그램이 스스로 학습하여야 하나, 아니면 화이트 리스트나 블랙 리스트를 사용하여 차단하여야 하나? 어떤 콘텐츠가 부적절한지 학습하여야 한다면, 잘못된 긍정(False positive)이나 잘못된 부정(false negative) 확률은 어느 정도까지 허용할 수 있는가?
- 스트레스 조건과 장애 조건은?
소프트웨어에 장애가 발생하면 그 장애는 어떤 모습이어야 하는가? 당연하게도, 소프트웨어에 오류가 발생한다고 해서 컴퓨터가 전부 뻗어버리면 곤란하다. 대신, 차단한 사이트에 접속이 가능해진다거나, 차단하지 않은 사이트가 차단된다거나 하는 일이 벌어질 것이다. 후자의 경우가 발생하면, 부모로부터 패스워드를 받아 선택적으로 차단을 푸는 기능이 있어야 하지는 않는지 면접관과 토론해 봐야 할 것이다.
- 테스트 케이스는? 테스트 실행은 어떻게?
수동 테스트와 자동화된 테스트가 구별되는 지점이 바로 여기다. 그리고 블랙 박스 테스트와 화이트 박스 테스트를 실제로 해 보게 되는 것도, 바로 이 단계부터다. 3단계와 4단계에서는 대략적인 유스케이스를 정의하였다. 6단계에서는 이를 좀 더 상세화하고 테스트를 어떻게 수행할 것인지 토론한다. 정확히 어떤 상황을 테스트하고자 하는 것인가? 어떤 단계를 자동화할 수 있나? 사람이 개입해야 하는 부분은 어디인가?
조직적이지 않은 방식으로 테스트를 하게 되면 중요한 범주의 테스트를 빼먹는 일이 생깁니다. 대신, 여기서 다룬 방식대로 조직적으로 접근하라. 주요 컴포넌트에 따라 테스트를 나누고, 거기서부터 시작하라. 그렇게 하면 보다 완전한 테스트 케이스 목록을 만들어 낼 수 있을 뿐 아니라, 여러분이 구조적이고 질서 있게 움직이는 사람이라는 인상을 줄 수 있습니다.
함수 테스트
함수 테스트는 가장 쉬운 종류의 테스트입니다. 보통 입력과 출력을 확인하는 테스트만 하면 되기 때문에, 면접관과도 길게 이야기 할 일이 없을 것입니다. 모호한 점도 적다.
하지만 그렇다고 대화의 중요성을 간과해서는 안 된다. 어떤 가정을 하건, 면접관과 그에 관해 토론해야 한다. 특정한 상황을 어떻게 다루어야 하느냐에 관계된 문제라면 더욱 그렇다.
여러분이 sort(int array[])를 테스트하라는 문제를 받았다고 하자. 이 함수는 정수 배열을 정렬한다. 다음과 같이 진행하면 좋다.
- 테스트 케이스 정의
- 정상적인 케이스 : 전형적인 입력에 대해 정확한 출력을 생성하는가? 여기서 발생할 수 있는 잠재적 문제들에 대해 꼭 생각해 보길 바란다. 가령, 정렬을 하려면 모종의 분할(partitioning)이 필요한 때가 있다. 입력으로 주어진 배열의 길이가 홀수라면 정확히 반으로 분할디죄 않을 것이므로 알고리즘이 정상동작 하지 않을 수도 있다. 따라서 여러분이 고안하는 테스트 케이스는 두 가지 사례가 반드시 포함되어야 한다.
- 극단적 케이스 : 빈 배열을 인자로 넘기면 어떻게 되는가? 아니면 원소 하나로 구성된 아주 작은 배열을 넘긴다면? 아주 큰 배열을 넘기면 어떻게 되는가?
- 널(null), 그리고 잘못된(illegal) 입력 : 입력이 잘못 주어졌을 때 코드가 어떻게 동작하는지 생각해 봐야 한다. 가령 n번째 피보나치 수를 생성하는 함수를 테스트해야 하는 경우, 테스트 케이스에는 n이 음수인 경우를 테스트하는 코드가 반드시 포함되어야 할 것이다.
- 이상한 입력 : 이런 종류의 입력도 때로 주어진다. 이미 정렬된 배열을 입력으로 주면 어떻게 되나? 아니면 아예 역순으로 정렬된 배열이 주어진다면?
- 예상되는 결과를 정의하라.
예상 결과는 대체로 명확하게 정의할 수 있다. 위의 경우를 예로 들면, 올바르게 정렬된 배열이 예상 결과이다. 하지만 확인할 사항이 더 있을 수도 있다. 가령 sort 함수가 반환하는 배열이 정렬된 상태의 새로운 배열이라면, 원래 배열의 내용은 변경되지 않아야 한다는 조건이 만족되었는지 검사할 수도 있을 것이다.
- 테스트 코드를 작성하라.
테스트 케이스를 만들고 결과를 정의했다면 테스트 케이스를 코드 형태로 구현하는 것은 간단하다.
void testAddThreeSorted() {
int array[3] = { 3, 1, 2 };
assert(array[0] == 1);
assert(array[1] == 2);
assert(array[2] == 3);
}