Home 11. 테스트 개요
Post
Cancel

11. 테스트 개요

테스트는 처음부터 프로그래밍과 함께였다. 하지만 오랫동안 테스트 프로세스는 크게 개선되지 못 했고, 대부분 수동으로 이루어져 오류가 나기 쉬운 프로세스였다. 2000년대 초부터 테스트 방식이 진화되었고 그 진회의 중심에는 개발자가 주도하는 테스트와 자동 테스트가 있었다.

자동 테스트는 버그가 몰래 숨어들어 고객을 놀라게 하는 시태를 막아준다. 또 새로운 기능을 추가하거나 코드가 더 건실해지도록 리팩터링하거나 대규모 재설계를 진행하는 상황에서 자동 테스트는 실수를 빠르게 잡아주므로 안심하고 소프트웨어를 변경할 수 있다.

테스트 체계가 잘 갖춰져 있다면 변화를 두려워할 이유가 없다. 따라서 테스트 체계를 소프트웨어 개발의 핵심 역량으로 취급할 수 있다. 시스템을 더 많이 더 빠르게 변경하고 싶다면 더 빠르게 테스트하는 방법을 모색해야 한다.

한편 테스트를 작성하는 행위가 시스템의 설계도 개선해준다. 자동 테스트를 작성하면 여러 문제를 개발 주기의 초반에 잡아내게 된다. 그 결과 모듈화가 더 잘되어 미래의 변화에 훨씬 유연한 소프트웨어가 만들어진다.

11.1 테스트를 작성하는 이유

테스트는 다음 요소들로 정의할 수 있다.

  • 테스트하려는 단 하나의 행위
  • 특정한 입력
  • 관측 가능한 출력 혹은 동작
  • 통제된 조건

간단한 테스트가 수백, 수천개 모이면(test suit) 제품이 전체적으로 의도한 설계대로 잘 작동하는지, 혹은 언제 그렇지 못한 지를 이야기할 수 있게 된다. 테스트 스위트를 건실하게 만들고 유지하는 데는 많은 노력이 든다. 코드베이스의 덩치가 커지면 테스트 스위트도 커지는데 이때 테스트 결과가 일관되지 못하거나 느려지는 문제가 나타나기도 한다. 이러한 문제를 해결하지 못하면 테스트 스위트의 존재가 위태로워진다.

테스트는 좋은 제품을 빠르게 만들 수 있게 해줄 뿐 아니라 우리 삶에서 중요한 제품과 서비스의 안전을 보장하는 데도 점점 핵심적인 역할을 하고 있다.

구글은 문제가 터진 후에야 테스트를 고민해서는 안 된다. 라고 결론을 지었다.

11.1.1 구글 웹 서버 이야기

283~284p

11.1.2 오늘날의 개발 속도에 맞는 테스트

대부분의 소프트웨어는 기능과 자원 플랫폼이 너무 폭증해서 사람이 모든 행위를 수동으로 검증할 수 있는 한계를 아득히 넘어섰다. 모든 기능을 사람이 조작하여 제품 품질을 확인하는 방식은 확장이 불가능하다. 그래서 테스트에서의 해법은 단 하나 자동화 뿐이다.

11.1.3 작성하고, 수행하고, 조치하라

가장 순수한 형태의 자동 테스트는 테스트 작성, 테스트 수행, 실패한 테스트에 대한 조치 이렇게 세 가지 활동으로 이루어 진다. 자동 테스트는 적은 양의 코드로 구성된다. 테스트 코드는 환경을 원하는 모습으로 설정하고, 시스템을 호출하고, 결과를 검증한다.

과거의 품질보증(QA) 프로세스와 달리 오늘날의 개발자들은 자신의 코드를 검사하는 자동 테스트를 작성하고 수행하는데 능동적이고 핵심적인 역할을 한다.

테스트 작성은 테스트 자동화 프로세스의 첫 번째 단계일 뿐이다. 테스트를 작성한 후에는 작성한 테스트를 수시로 실행해야 한다. 자동 테스트의 핵심은 같은 동작을 끊임없이 반복하는 데 있다. 그리고 테스트 프로세스가 얼마나 효과적이냐는 이러한 테스트 실패를 어떻게 처리하느냐에 달려 있다. 실패 테스트가 해결되지 못하고 빠르게 쌓여간다면 테스트에 투자한 노력이 허사가 되니 그렇게 되지 않도록 하는 것이 중요하다.

11.1.4 테스트 코드가 주는 혜택

  • 디버깅 감소 - 테스트를 거친 코드는 통상적으로 결함이 적다. 테스트를 한 번 작성해두면 프로젝트가 살아 있는 내내 결함을 예방해주고 디버깅에서 해방시켜주는 식으로 지속해서 혜택을 준다.
  • 자신 있게 변경 - 좋은 테스트들로 무장한 팀은 자신감을 가지고 변경들을 리뷰하고 수용할 수 있다.
  • 더 나은 문서자료 - 한 번에 하나의 행위만 집중해 검증하는 명확한 테스트는 마치 실행 가능한 문서와 같다. 코드가 특정 상황에서 어떻게 동작하는지 궁금하다면 그 상황을 검증하는 테스트를 보면 된다.
  • 더 단순한 리뷰 - 코드 리뷰시 다양한 측면에서 코드를 검사해주는 테스트가 준비되어 있다면 리뷰어가 변경된 코드가 제대로 작동하는지를 검증하는 시간을 크게 줄여준다.
  • 사려 깊은 설계 - 새로 작성한 코드의 테스트를 작성하는 일은 실질적으로 해당 코드의 API가 잘 설계되어 있는지를 시험하는 행위이다.
  • 고품질의 릴리스를 빠르게

11.2 테스트 스위트 설계하기

엔지니어들은 커다란 시스템 규모의 테스트를 작성하는 편을 선호하지만, 이러한 테스트는 작은 테스트와 비교하여 느리고 신뢰도가 낮고 디버깅하기도 어렵다. 고통을 줄이고자 하는 욕구가 엔지니어들을 점점 더 작은 테스트를 작성하도록 이끌었다. 그러면서 더 작은 테스트가 더 빠르고, 안정적이고, 평균적으로 고통이 적다는 걸 깨우쳤다.

테스트에서 작다는 의미에는 두 가지 요소가 있다. ‘크기’, ‘범위’

11.2.1 테스트 크기

테스트의 크기를 가늠하는 기준은 코드 줄 수가 아니라 어떻게 동작하고, 무엇을 하고, 얼마나 많은 자원을 소비하는지로 평가한다.

테스트 스위트에 바라는 품질은 속도와 결정성이다. 실제로 범위와 상관없이 작은 테스트는 더 많은 인프라나 자원을 사용하는 테스트보다 거의 항상 더 빠르고 더 결정적이다.

작은 테스트

가장 작은 테스트는 제약이 가장 엄격 하다. 가장 중요한 제약은 바로 테스트가 단 하나의 프로세스에서 실행되어야 한다는 것이다.

서버를 두고 독립된 테스트 프로세스에 연결해 수행하는 방식도 허용되지 않고, 또 데이터베이스와 같은 제 3의 프로그램을 수행해서도 안 된다. 그리고 작은 테스트는 sleep, I/O 연산 같은 블로킹 호출을 사용해서는 안 된다. (네트워크, 디스트에 접근 불가) 그래서 블로킹 호출을 수반하는 대상을 검사하는 테스트 코드는 테스트 대역을 사용해야 한다.

제약이 과한 이유는 작은 테스트 케이스 수백개로 이루어진 테스트 스위트가 하루종일 실행되는 환경에서 불규칙한 테스트가 단 몇 개만 있어도 원인을 찾아 헤매느라 생산성이 급격하게 떨어질 것이기 때문이다.

중간 크기 테스트

중간 크기 테스트는 여러 프로세스와 스레드를 활용할 수 있고, 로컬 호스트로의 네트워크 호출 같은 블로킹 호출도 이용할 수 있다. 단, 외부 시스템과의 통신은 여전히 불허하다.

유연성이 커지면 반대급부로 테스트는 느려지고, 비결정적이 될 가능성이 높아진다. 여러 프로세스에 걸쳐 있거나 블로킹 호출을 하기 시작하면 운영체제나 서드파티 프로세스에 의존하게 된다. 외부 요인이 개입되므로 성능과 결정성을 온전히 우리 스스로가 보장할 수 없다는 뜻이다.

큰 테스트

큰 테스트는 로컬 호스트 제약에서 해방되어, 테스트와 대상 시스템이 여러 대의 기기를 활용할 수 있게 된다. 더 유연해지는 만큼 위험도 늘어나 여러 기기에 걸쳐있는 시스템을 네트워크로 연결해 다루게 되면서 단일 기기에서 구동할 때보다 느려지거나 비결정성이 커질 가능성이 훨씬 높아진다.

테스트 크기와 무관한 공통 특성

모든 테스트는 setup, execute, tear down 하는데 필요한 모든 정보를 담고 있어야 한다. 또 테스트 수행 순서 같은 외부 환경에 관해서는 가능한한 아무것도 가정하지 않아야 한다.

테스트는 확인하려는 행위를 수행하는 데 필요한 정보만을 포함해야 한다. 깔끔한 코드는 테스트 실패 원인을 진단하는 데도 도움이 된다. 테스트는 무엇을 검사하는지가 명확해야 한다.

실제 상황에서의 테스트 크기

테스트 크기를 명확히 정의한 덕분에 구글은 규칙을 실무에 적용할 수 있는 도구들을 만들 수 있었다.

11.2.2 테스트 범위

테스트 범위란 주어진 테스트가 얼마나 많은 코드를 검증하느냐를 말한다. 좁은 범위 테스트는 독립된 클래스나 메서드 같이 코드베이스 중 작은 일부 로직을 검증하도록 설계된다. 중간 범위 테스트는 적은 수의 컴포넌트들 사이의 상호작용을 검증하도록 설계된다. 넓은 범위의 테스트는 시스템의 서로 다른 부분들 사이의 상호작용, 혹은 클래스나 메서드 하나만 실행할 때는 괜찮다가 여럿을 조합해 실행하면 나타나는 예기치 못한 동작을 검증하도록 설계된다.

단위 테스트의 범위가 좁다고 할 때는 실행되는 코드가 아니라 검증되는 코드의 양이 기준이다.

구글은 되도록 작은 테스트를 추구하며, 마찬가지로 좁은 범위 테스트를 추구한다. 단위 테스트는 빠르고 안정적이며 범위를 극적으로 좁혀줘서 클래스나 함수가 제공하는 동작들을 적은 노력으로도 모두 식별하게 해준다. 주의해야할 안티패턴으로 아이스크림콘과 모래시계가 있다.

아이스크림 콘에서는 엔지니어들이 종단간 테스트를 많이 작성하고 통합 테스트나 단위 테스트는 훨씬 적게 작성한다. 이런 테스트 스위트는 일반적으로 느리고 신뢰할 수 없으며 고치기도 어렵다.

모래시계는 종단간 테스트와 단위 테스트는 많지만 통합 테스트가 적다. 모래시계는 아이스크림 콘만큼 나쁘지는 않다. 모래시계 패턴은 구성요소들이 강하게 커플링되어 각각의 인스턴스를 독립적으로 만들어낼 수 없을 때 나타난다.

11.2.3 비욘세 규칙

어떤 행위나 속성을 테스트해야 할까? 깨뜨려보고 싶은 모든 것을 테스트하라

비욘세 규칙은 네가 좋아했다면 CI 테스트를 준비해뒀어야지라는 뜻이다. 문제를 수정하고, 검증하는 테스트를 추가할 책임은 전적으로 코드를 작성한 자에게 있다.

11.2.4 코드 커버리지

코드 커버리지는 어느 테스트가 기능 코드의 어느 라인을 실행하는지를 측정하는 수단이다. 코드 커버리지는 테스트 품질을 파악하는 표준 지표로 간주되기도 하나 적은 수의 테스트만으로 상당량의 라인을 실행하면서도 의미 있는 동작은 거의 돌려보지 않을 수 있고, 또한 코드 커버리지는 호출된 라인 수만 셀 뿐, 실행 결과로 어떤 일이 벌어졌는지 고려하지 않는다.

테스트 스위트의 품질을 높이는 더 나은 방안은 무엇일까? 바로 검사해야 할 행위에 집중하는 것이다.

11.3 구글 규모의 테스트

구글은 모든 코드를 모노리포 즉 하나의 리포지터리에서 관리한다. 구글의 코드베이스에서는 매주 약 2천 5백만 라인이 변경된다. 그중 절반 가량은 수만 명에 이르는 엔지니어들의 손가락에서 시작되며, 나머지는 자동화 시스템이 변경한다.

또 코드베이스를 열어두면 코드베이스를 모두가 함께 책임지는 공동 소유 의식이 싹튼다.

또 구글의 다른 독특한 점으로 리포지토리 브랜치를 사용하는 팀이 거의 없다. 모든 변경이 리포지토리 헤드에 직접 커밋되어 변경 즉시 모두가 볼 수 있다. 그리고 모든 소프트웨어는 테스트 인프라가 검증한 가장 최신 커밋까지 반영해 빌드된다.

11.3.1 대규모 테스트 스위트의 함정

코드베이스가 커지다 보면 기존 코드를 변경하는 일을 피할 수 없다. 자동 테스트가 엉망으로 작성되어 있다면 이럴 때 코드를 변경하기 어렵다. 특히 깨지기 쉬운 테스트가 우리를 가로막는다. 깨지기 쉬운 테스트를 만드는 주범으로 모의 객체 오용을 들 수 있다.

깨지기 쉬운 테스트로 인한 저항 외에도 테스트 스위트가 커지면 수행 시간이 길어진다는 점도 문제이다. 느려질수록 수행 빈도는 자연스럽게 줄어들 것이고, 테스트의 가치는 그만큼 작아진다.

테스트 스위트가 비결정적이고 느려지면 생산성을 갉아 먹는다. 테스트 스위트가 득보다 실이 많다면 엔지니어들은 결국 테스트를 실행하지 않고서라도 구현 작업을 끝마칠 수 있는 방법을 찾으려 할 것이다. 거대한 테스트 스위트를 잘 관리하는 비결은 바로 테스트를 존중하는 문화이다. 엔지니어들에게 테스트에 관심을 갖도록 장려하자

문화를 가꾸는 일과 더불어 린터를 개발하거나 문서자료를 보강하는 등, 안 좋은 테스트를 만드는 실수를 줄여주는 테스트 인프라에도 투자해야 한다.

11.4 구글의 테스트 역사

11.4.1 오리엔테이션 수업

구글의 초기 엔지니어 상당수가 테스트를 기피했다. 구글에서는 모든 신규 엔지니어가 반드시 거쳐야 하는 제도가 있었다. 바로 입사 오리엔테이션이다. 구글 초기에는 오리엔테이션 프로그램의 대부분이 의료혜택이나 구글 검색의 작동 방식 같은 주제로 다뤘다. 하지만 2005년을 시작으로 자동 테스트의 가치를 이야기하는 한 시간짜리 수업이 추가되었다. 이때 중요한 것은 이 모든 것이 구글 표준 관행인 것처럼 교육을 했다는 것이다.

오리엔테이션을 마친 엔지니어들은 팀에 배정되어 배운 대로 테스트를 작성하기 시작했고, 그렇지 않은 팀원들에게 질문을 던지곤 했다. 이 후 1~2년 만에 오리엔테이션을 거친 엔지니어 수가 기존 엔지니어의 수를 넘어섰고, 올바른 토대 위에서 새로 시작하는 프로젝트가 많아졌다.

11.4.2 테스트 인증

초기에는 테스트 품질이 너무 많이 떨어져서 테스트하기가 거의 불가능했다. 그래서 테스팅 그룹릿은 테스트 인증이라는 인증 프로그램을 만들었다. 테스트 인증의 목적은 각 팀이 자신의 테스트 프로세스 수준을 알게 하고 한 단계 올라서기 위한 지침을 제공하는 것이었다.

이 프로그램은 5개 레벨로 구성되며 해당 레벨로 인정 받는데 필요한 구체적인 조건을 정의했다.

테스트 인증 레벨1이 되려면 가장 기본이 되는 조건 몇 가지를 충족해야 한다. 지속적 빌드 구축, 코드 커버리지 추적, 모든 테스트를 작은/중간 크기/큰 테스트로 구분, 불규칙한 테스트 식별, 바로 실행할 수 있는 빠른 테스트 스위트 마련이다. 레벨5가 되려면 모든 테스트를 자동화하고, 모든 커밋 전에 빠른 테스트 스위트가 수행되도록 하고, 비결정성을 완전히 제거하고, 모든 행위를 테스트해야 한다.

11.4.3 화장실에서도 테스트

테스팅 그룹릿이 구글의 테스트 문화 개선을 위해 취한 모든 활동 중 화장실에서도 테스트(TotT)만큼 참신한 건 없을 것이다. TotT의 목표는 회사 전반의 테스트에 대한 인식을 적극적으로 높이는 것이었다. 처음에는 정기적인 이메일 뉴스레터를 고려했으나 이미 구글 직원들은 메일의 홍수 속에 살고 있었기 때문에 관심 가지고 챙겨볼 직원은 거의 없을 것 같았다. 농담처럼 나온 화장실 변기 앞에 전단지를 붙여두자는 아이디어를 시작으로 TotT가 생겼다.

TotT는 빠르게 구글의 대표적인 문화로 자리 잡았고, 현재까지 세계에 퍼져있는 구글 엔지니어들은 테스트에서 상상할 수 있는 거의모든 측면을 다루는 수백 편의 TotT를 제작했다.

11.4.4 오늘날의 테스트 문화

구글의 테스트 문화는 2005년부터 먼길을 걸어왔다. 신규 입사자 오리엔테이션에서는 여전히 테스트를 가르치고, 거의 매주 새로운 TotT가 배포되는 것도 똑같다.

구글에서는 코드를 변경할 때마다 코드 리뷰를 거쳐야 한다. 그리고 모든 변경에는 테스트 코드가 포함되어 있어야 한다. 그렇다고 리뷰어가 테스트가 누락되었단 이유만으로 변경을 거부할 수는 없다. 또 테스트 인증 프로그램을 대체하고자 프로젝트 건실성(pH)란 개념이 생겼다.

11.5 자동 테스트의 한계

모든 종류의 테스트를 다 자동화할 수는 없다. 창의력이 필요한 분야에서 인간이 더 뛰어날 수 있다. 이를 일반화한 용어가 탐색적 테스팅이다.

탐색적 테스팅은 기본적으로 창의력을 요구하는 작업으로 검사 대상을 마치 고장내야 할 퍼즐로 취급한다. 탐색적 테스팅으로 문제를 발견하는 즉시 자동 테스트를 추가하여 문제가 재발하지 않도록 예방해야 한다.

11.6 마치며

개발자 주도 자동 테스트는 구글에서 가장 혁신적인 소프트웨어 엔지니어링 관행 중 하나였다. 이 덕분에 구글은 더 큰 팀을 구성해 더 큰 시스템을 생각보다 빠르게 구축해냈다.

11.7 핵심 정리

  • 자동 테스트는 소프트웨어를 변경할 수 있게 해주는 토대이다.
  • 테스트를 확장하려면 반드시 자동화해야 한다.
  • 테스트 커버리지를 건실하게 유지하려면 균형 잡힌 테스트 스위트가 필요하다.
  • 조직의 테스트 문화를 바꾸는 데는 시간이 걸린다.
This post is licensed under CC BY 4.0 by the author.

10. 문서자료

12. 단위 테스트