logo
Search검색어를 포함하는 게시물들이 최신순으로 표시됩니다.
    Table of Contents
    11장: 테스트 개요

    이미지 보기

    11장: 테스트 개요

    구글의 테스트 개발 문화에 대하여

    • 25.07.06 작성

    • 읽는 데 24

    TOC

    참고

    본 내용은 구글 엔지니어는 이렇게 일한다(링크)를 읽고 정리한 내용입니다.
    책의 내용과 함께 개인적인 의견과 생각, 학습을 담아 작성하였습니다.

    들어가며

    • 개발 주기에서 버그를 발견하는 시기가 늦어질수록 고치는 비용이 커진다.
    • 시스템을 더 많이 더 빠르게 변경하고 싶다면 더 빠르게 테스트하는 방법을 모색하라.
      • 테스트 체계가 잘 갖춰져 있다면 변화를 두려워할 이유가 없다.
    • 테스트를 작성하면 모듈화가 더 잘 되어 시스템의 설계에도 도움을 준다.

    단위테스트 책을 봤을 때와 비슷한 내용이 나와서 반갑네요.

    테스트를 작성하는 이유

    • 테스트는 엔지니어에게 신뢰를 줄 때에만 가치가 있다.
    • 나쁜 테스트는 아예 없는 것만 못하다.
    • 구글은 테스트를 엔지니어링 문화의 중심에 두고 있다.

    구글 웹 서버 이야기

    • 릴리즈 때마다 버그가 넘치던 구글 웹 서버
    • 모든 코드 변경에 지속해서 실행할 수 있는 테스트를 담도록 정책 변경
    • 핵심은 제품 결함 해결을 프로그래머의 능력에만 의존해서는 안 된다는 사실
    • 엔지니어의 시야는 보고된 버그와 주변 코드에 집중된다.
      • 복잡한 시스템에서는 버그를 고치는 과정에서 다른 버그를 만들어 넣곤 한다.

    테스트가 서비스 전체 품질을 유지하며 안전하게 소스코드를 변경할 수 있도록 하기 때문에 리팩토링의 품질과 시간에 절대적으로 중요하다는 사실을 들었습니다. 테스트는 진짜 이 갈고 한 번은 해봐야겠어요.

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

    • 모든 기능 점검을 사람이 조작해서 품질을 확인하는 것은 불가능하다.
    • 테스트에서의 해법은 단 하나, 바로 '자동화' 뿐이다.

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

    • 가장 순수한 형태의 자동 테스트는 '테스트 작성', '테스트 수행', '실패한 테스트에 대한 조치' 이렇게 세 단계로 이루어진다.
    • 자동 테스트는 적은 양의 코드로 구성된다.
    • 오늘날의 시스템의 규모와 배포 속도를 따라잡으려면 모든 엔지니어가 테스트도 함께 개발해야 한다.
    • 테스트 절차까지 '실행 가능한 코드'로 작성해두면 코드가 변경될 때마다, 하루에 수천 번도 더 돌릴 수 있다. 기계는 지치지 않으니까.
    • 테스트 프로세스가 얼마나 효과적이냐는 테스트 실패를 어떻게 처리하느냐에 달려 있다.

    테스트가 신뢰를 가져야 한다는 말 역시 익히 들은 말이었는데, 이 실패한 테스트를 어떻게 취급하고 처리하느냐가 테스트 프로세스의 존재 가치를 제공한다는 사실이 인상적이었습니다. 맞는 말입니다.

    테스트 코드가 주는 혜택

    테스트에 시간을 투자하는 것은 개발자 생산성을 향상시킨다. 그런 조직을 경험해보지 못한 개발자는 동의하지 못하겠지만

    디버깅 감소

    • 테스트를 거친 뒤의 코드는 결함이 적다.
    • 테스트를 한 번 작성해두면 결함을 예방하고 디버깅에서 해방시켜준다.

    자신 있게 변경

    • 좋은 테스트로 무장한다면 자신있게 코드를 변경, 리뷰, 수용할 수 있다.
    • 테스트들이 프로젝트의 주요 기능들을 끊임없이 검증한다.

    더 나은 문서자료

    • 대부분의 문서자료는 코드를 잘 대변하지 못한다. (낡거나, 일부를 누락하는 등)
    • 명확한 테스트는 마치 실행 가능한 문서와 같다.
    • 특히 요구사항의 변경으로 코드가 변경됐을 때 기존 테스트를 통과하지 못한다면 문서가 낡았음을 인지하고 수정할 수 있다.

    더 단순한 리뷰

    • 코드리뷰 시에 변경된 코드가 제대로 작동하는지를 검증하는 시간이 줄어든다.
    • 테스트의 수행이 통과했는지만 보면 되기 때문

    사려 깊은 설계에도

    • 테스트하기 어려운 코드는 너무 많은 역할을 짊어지거나 의존성을 관리하기 어렵게 짜여있기 때문일 가능성이 높다.
    • 잘 설계된 코드는 모듈화가 잘 되어 있어야 한다.
    • 다른 코드와 강하게 결합되지 않고, 특정 역할에 집중해야 한다.

    테스트 스위트 설계하기

    • 테스트의 크기를 키워 시스템 규모가 되면 느리고, 신뢰하기도, 디버깅도 어려워진다.
    • 더 작은 테스트를 작성하는 것은 고통을 줄이고자 하는 욕구
    • 모든 테스트 케이스에는 두 가지 독립된 요소가 있다. 크기와 범위.
      • 크기: 테스트 케이스 하나를 실행하는 데 필요한 자원(메모리, 프로세스, 시간 등)
      • 범위: 검증하려는 특정한 코드 경로

    테스트 크기

    • 어떻게 동작하고, 무엇을 하고, 얼마나 많은 자원을 소비하는지로 구분
    • 작은, 중간, 큰 테스트로 구분한다.
      • 작은 테스트: 프로세스 하나
      • 중간 테스트: 기기 하나
      • 큰 테스트: 자원을 원하는만큼 사용
    • 구글이 테스트 스위트에서 바라는 품질은 바로 속도와 결정성

    작은 테스트

    • 제약이 가장 엄격
    • 테스트가 단 하나의 프로세스에서 실행되어야 한다.
    • 제한사항
      • 서버를 두고 독립된 테스트 프로세스에 연결해 수행
      • DB와 같은 제3의 프로그램을 수행
      • sleep, I/O 연산 같은 블로킹 호출
      • 네트워크와 디스크에 접근
    • 제약의 목적
      • 테스트를 느리게 하거나 비결정적으로 만드는 주요 원인들로부터 작은 테스트를 떼어 놓는 것
      • CPU가 실행할 수 있는 최고 속도로 실행

    중간 크기 테스트

    • 여러 프로세스와 스레드를 사용 가능
    • 로컬 호스트로의 네트워크 호출 등 블로킹 호출도 이용 가능
    • 외부 시스템과의 통신은 여전히 불허(단 한 대의 기기에서 수행되어야 할 것)
    • 여러 프로세스를 사용할 수 있다면 할 수 있는 일이 크게 늘어난다.
      • DB 인스턴스를 실행하여 대상 코드를 현실적으로 검증 가능
      • 웹 UI와 서버 코드의 조합도 테스트 가능
      • 웹 앱 테스트에 웹 드라이버 같은 도구를 이용 가능
    • 유연성이 커지면 반대로 테스트는 비결정적이 될 가능성이 높다.
      • 외부 요인이 개입되므로 성능과 결정성을 온전히 우리 스스로가 보장할 수 없다.
      • 중간 크기 테스트는 원격 기기로의 네트워크 호출을 불허함으로써 어느 정도의 보호막은 유지하고 있다.

    큰 테스트

    • 테스트와 대상 시스템이 여러 대의 기기를 활용할 수 있게 된다.
    • 더 유연해지는 만큼 위험도 늘어난다. 더 느리고 비결정적이 된다.
    • 구글은 큰 테스트를 작은 테스트나 중간 크기 테스트와 분리하여, 빌드나 릴리스 때만 수행되도록 한다.
      • 개발 워크플로에 영향을 주지 않도록 하기 위해

    단위 테스트의 기술 책에서 봤던 1:100의 원칙이 생각나네요. 통합 테스트를 1개 작성한다면 단위 테스트는 그 사이에 100개를 작성했어야 한다는 규모의 구분입니다.

    불규칙한 테스트는 비싸다

    • 테스트 결과가 불규칙한 상황이 계속되면, 생산성 저하를 넘어 테스트의 신뢰를 잃게 된다.
    • 이러면 테스트가 실패하더라도 더는 신경 쓰지 않는다.
    • 경험상 불규칙한 실패가 1%에 이르면 테스트의 가치가 바래기 시작
    • 대부분의 불규칙한 실패는 테스트 자체에 비결정적으로 동작하는 로직이 있어서 발생

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

    • 모든 테스트는 '밀폐'되어야 한다.
    • 셋업(set-up), 실행(execute), 티어다운(tear-down) 하는 데 필요한 모든 정보를 담고 있어야 한다.
    • 테스트 수행 순서 같은 외부 환경에 관해서는 가능한 아무것도 가정하지 않아야 한다.
      • 예를 들어 공유 DB에 의존해서는 안 된다.
      • 이 때문에 더 큰 테스트를 작성하기 어려워지는 것은 맞지만, 그럼에도 노력은 이어가야 한다.
    • 테스트는 확인하려는 행위를 수행하는 데 필요한 정보'만'을 포함해야 한다.
    • 테스트는 무엇을 검사는지가 명확해야 한다.
    • 테스트에서는 조건문이나 순환문 같은 제어문을 쓰지 않는 게 좋다.
      • 복잡한 테스트일수록 버그가 숨어들 가능성이 커진다.
    • 테스트는 오직 실패했을 때만 다시 들여다 본다.
    • 누군가 테스트 코드를 읽었을 때 부끄럽지 않도록 작성하라.

    테스트 범위

    • 주어진 테스트가 얼마나 많은 코드를 검증하느냐
    • 범위에 따라 이렇게 구분된다.
      • 좁은 범위 테스트: 독립된 클래스나 메서드처럼 코드베이스 중 작은 일부 로직을 검증
      • 중간 범위 테스트(통합 테스트): 적은 수의 컴포넌트들 사이의 상호작용을 검증
      • 넓은 범위 테스트(E2E 테스트): 시스템의 서로 다른 부분들 사이의 상호작용, 클래스의 조합 등을 검증
    • 단위 테스트의 범위가 좁다고 할 때는 '검증'되는 코드의 양이 기준
    • 테스트의 범위가 커질수록 전체 테스트에서 차지하는 비율은 적어지는 피라미드 모양이 되어야 한다.

    비욘세 규칙

    비욘세 규칙: "네가 좋아했다면 감당해야지"

    속뜻: 전체 코드베이스의 변경을 책임지는 인프라팀이 자주 활용. 개발자가 만들었다면 개발자가 CI 테스트를 준비해야 한다. 인프라팀은 코드에 대한 실패를 감당하지 않는다.

    Q. 어떤 행위나 속성을 테스트해야 하나요?"
    A. "깨뜨려보고 싶은 모든 것을 테스트하세요"

    • 시스템이 특정 행위를 올바로 수행하는지 확신하고 싶다면 행위를 검증하는 자동 테스트를 작성하라.
    • 성능, 행위 정확성, 접근성, 보안처럼 의심할 수 있는 모든 것을 포함한다.

    코드 커버리지

    • 어느 테스트가 기능 코드의 어느 라인을 실행하는지를 측정하는 수단
    • 코드 커버리지가 테스트 품질을 파악하는 표준 지표로 간주되는 것은 안타까운 일
      • 테스트가 실행하는 코드 라인 수가 실행 결과를 방증하는 것은 아니기 때문
      • 큰 테스트는 커버리지 인플레이션을 일으킨다.
      • 더 큰 문제는 커버리지 자체가 목표가 되기 싶기 때문
    • 테스트 스위트의 품질을 높이는 더 나은 방안
      • 검사해야 할 행위에 집중하는 것

    결론: 코드 커버리지는 테스트되지 않은 코드가 어디인지는 알려줄 수 있으나, 시스템이 얼마나 제대로 테스트되었느냐를 판가름하는 지표로는 적합하지 않다.

    구글 규모의 테스트

    • 구글에서는 모노레포를 사용하고 있고, 엔지니어가 코드에 마음껏 접근하도록 제약을 두지 않는다.
    • 코드베이스를 열어두면 모두가 함께 책임지는 공동 소유 의식이 싹튼다.
    • 제품이나 서비스에 버그가 발견되면 직접 수정할 수 있는 개방적인 운영이다.
    • repository branch를 사용하는 팀이 거의 없다. 모든 변경이 repository head에 직접 커밋되어 변경 즉시 모두가 볼 수 있다.

    세부 브랜치 없이 main 브랜치에 바로 녹인다는 얘기처럼 보이는데 Trunk Based Development 같은 말인가 싶네요. 버전 관리는 어떻게 하는 걸까요.

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

    • 깨지기 쉬운 테스트는 코드 변경을 가로막는다.
      • 예상 결과를 너무 세세하게 표현하거나 광범위하고 복잡한 상용구가 덕지덕지한 테스트
    • 품질이 낮은 테스트는 해당 테스트와 관련 없는 코드가 변경되어도 실패할 수 있다.
    • 테스트가 느려지면 자주 수행하지 못하고 테스트의 가치는 낮아진다.
    • 깨지기 쉬운 테스트를 만드는 주범은 모의 객체 오용이 있다. 모의 객체의 한계를 이해하고 오용하지 말라.
    • 비결정적인 테스트를 위해 sleep, setTimeout 등의 함수를 호출하지 말라. 차라리 polling과 setTimeout을 결합해 사용하라.
    • 거대한 테스트 스위트를 관리하는 비결은 테스트를 존중하는 문화
      • 엔지니어들이 테스트에 관심을 갖도록 장려하라.
      • 테스트를 견고하게 만든 엔지니어에게 보상하라.
      • 테스트도 제품 코드처럼 다뤄야 한다.
      • 간단한 코드 변경에도 시간이 제법 소요된다면 테스트를 더 견고하게 만들라.
    • 안 좋은 테스트를 만드는 실수를 줄여주는 테스트 인프라에도 투자해야 한다.
      • 린터를 개발하거나, 문서 자료를 보강하는 등

    구글에서는 테스트를 잘 작성하고 유지하는 데에 많은 노력을 기울이고 있다는 사실이 잘 와닿는 것 같습니다.

    구글의 테스트 역사

    구글에서 자동테스틀 전사적으로 뿌리내리게 한 원동력을 소개한다.

    오리엔테이션 수업

    • 신규 엔지니어가 반드시 거쳐야 하는 제도가 하나 있다. 바로 입사 오리엔테이션이다.
    • 오리엔테이션에서 테스트의 가치와 올바른 작성법을 '마치 구글 표준인 것처럼' 교육 받는다. 그리고 마치 트로이목마처럼 테스트가 갖춰지지 않은 팀에 들어가서 테스트를 작성하며 파이를 높여갔다.

    구글에서는 어떻게 보면 교활하게, 어떻게 보면 똑똑하게 제도들을 개선하고 사람을 다루고 있네요. 재미 있는 일화 같습니다.

    테스트 인증

    • 테스트 인증(test certified)이라는 인증 프로그램을 만들었다.
    • 목적은 각 팀이 자신의 테스트 프로세스 수준(성숙도)을 알게 하고, 한 단계 올라서기 위한 지침을 제공하는 것(핵심)
    • 사내 대시보드의 각 팀의 현재 테스트 레벨을 보여주어 경쟁 분위기를 부추겼ㄷ.

    화장실에서도 테스트

    • 화장실에 짧고 핵심적인 이야기를 적어내는 방식을 고안. 초기 비용이 적고 참신해서 적용이 빨랐다.
    • 개인 공간을 침해했다는 반발도 있었으나, 회사 내외부로 커다란 반향을 몰아왔다.
    • 훌륭한 블로그 글로 이어졌고 개선 버전을 공개 게시하기 시작하며 구글의 경험을 업계 전체와 공유하는 데에도 기여했다.

    오늘날의 테스트 문화

    • 모든 변경에는 테스트 코드가 포함되어 있어야 한다.
    • 리뷰어는 품질과 정확성 모두를 검토하며, 테스트가 누락되어 있으면 변경을 거부할 수 있다.
    • 테스팅 그룹릿은 테스트를 강제하라고 제안하려 했으나, 구글 문화에 심하게 반하였기 때문에 철회. 훌륭한 생각은 반드시 퍼져나갈 것임을 믿고, 테스트가 훌륭한 아이디어임을 입증하는 데 초점을 맞췄다.

    자동 테스트의 한계

    • 모든 종류의 테스트를 다 자동화할 수는 없다.
    • 종종 사람에게 판단을 맡겨야 하는 음성, 영상 통화 품질 등의 평가, 창의력에 대한 부분이 있다.

    총평

    테스트에 대한 이야기를 길게 다뤘고, 앞으로 이어지는 3개 장에서도 테스트에 대한 이야기를 다룰 것이라고 예고했습니다. 바로 직전에 읽었던 단위 테스트의 기술과 상당 부분 겹치는 내용이 많아 익숙하기도 하고, 또 한 번 일깨워주는 효과도 있었습니다.

    지금은 회사에서 기능을 치느라 바쁜데, 단위 테스트의 기술 책을 읽을 당시에도 현업에는 거의 적용을 못 했던 것이 생각나서, 조금이라도 회사 코드에 테스트를 녹여봐야겠다는 생각을 합니다. 최근에 AI를 활용하는 방식에 TDD처럼 요구사항에 대한 테스트 코드를 먼저 작성하고, 이를 AI가 만족하는 코드를 작성하게 한다는 방법론을 봤습니다. 테스트 코드 작성과 AI 활용이라는 2마리 토끼를 다 잡을 수 있을 것 같아 바이브 개발 중에 최대한 녹여봐야겠습니다.

    profile

    FE Developer 박승훈

    노력하는 자는 즐기는 자를 이길 수 없다