logo
Search검색어를 포함하는 게시물들이 최신순으로 표시됩니다.
    Table of Contents
    9장: 가독성

    이미지 보기

    9장: 가독성

    가독성 좋은 테스트를 작성하는 방법

    • 25.04.07 작성

    • 읽는 데 11

    TOC

    참고

    본 내용은 단위 테스트의 기술을 읽고 정리한 내용입니다.
    책의 내용과 함께 개인적인 의견과 생각, 학습을 담아 작성하였습니다.

    가독성의 요소

    • 단위 테스트 이름 짓기
    • 변수 이름 짓기
    • 검증(assert)과 실행(action) 분리
    • 초기화 및 설정 해제

    하나씩 살펴보자

    단위 테스트 이름 짓기

    테스트가 포함된 파일 구조에는 다음 세 가지 중요한 정보가 포함되어야 한다.

    • 작업 단위의 진입점(혹은 현재 테스트 중인 기능 이름)
    • 진입점을 테스트하는 상황
    • 작업 단위의 종료점이 실행해야 하는 동작

    테스트 이름에서 이 정보들을 포함하면 범위, 기능, 역할 등을 쉽게 알 수 있다. 예를 들어 '진입점 X를 null값으로 호출하면, Y를 실행한다.' 형식으로 작성한다.

    위의 세 가지 요소는 테스트를 읽는 사람이 쉽게 볼 수 있는 곳에 있어야 한다.

    • 하나의 테스트 함수 이름에 포함시키기
    • 중첩된 describe 블록을 사용해서 나누기
    • 단순히 문자열 설명을 매개변수나 주석으로 사용하여 테스트에 포함시키기

    사용하는 언어와 가독성

    역자 - 영어를 써라

    역자: "테스트 이름은 사용하는 언어에 따라 가독성이 달라진다."

    • 언어마다 배치와 읽는 방향이 다르다.
    • 영어는 동사가 먼저 나오지만, 한국어는 맨 뒤에 나온다.
    • 중요한 동사가 먼저 나오는 게 명확하다.

    과연 그럴까

    저의 의견은 조금 다릅니다.

    역자의 의견을 완전히 반박하기는 힘듭니다. 하지만, 저는 상황에 따라 달라질 필요가 있지 않을까 싶습니다. 만약 통일감을 위해 영어와 한국어 중 하나를 선택해야 한다면, 저는 한국어를 선택하겠습니다.

    한국인 팀에서는 영어가 제 1언어가 아니기 때문에 개인마다 영어의 수준이 다를 수 있겠죠. 영어 해석 절차가 필요하다면 이 자체로 가독성을 해칠 수 있다고 봅니다.

    저는 한국어가 명확하다고 봐요. 단, 이것 역시 역자처럼 저의 의견입니다. 비교의 기준은 '동사가 먼저 온다는 이점이, 한국어를 영어로 씀으로써 떨어트리는 가독성보다 더 클 것인가' 일 것 같네요.

    한국어 쓸 거면 이렇게 해라

    역자는 한국어를 쓸 것이라면 이렇게 하라고 합니다.

    // describe() 블록을 사용하지 않는 경우
    it("진입점 X를 null값으로 호출하면, 작업 단위의 종료점에서 Y가 수행되어야 한다.", () => {
      // ...
    });
    
    // describe() 블록을 사용하는 경우
    describe("진입점 X를 테스트할 때", () => {
      describe("null 값으로 호출하면", () => {
        it("Y를 수행해야 한다.", () => {
          // ...
        });
      });
    });
    

    명심할 점

    "이름을 명확히 지어라."

    • 코드 가독성을 높여라.
    • 다른 개발자가 테스트 이름만으로도 무엇을 테스트하는지 알게 하라.
    • 파이프라인에서 실패했을 때 테스트 이름이 보인다.
    • 특별한 디버깅 없이 오류 로그만으로 원인을 알 수 있다.

    매직 넘버와 변수 이름

    용어 설명

    • 매직 넘버: 하드코딩된 값, 기록에 남지 않은 값, 명확하게 이해되지 않는 상수나 변수
    • 매직: 값들이 동작하지만 이유를 알 수 없다. 마치 마법처럼.

    매직 넘버 사용

    describe("password verifier", () => {
      test("on weekends, throws exceptions", () => {
        expect(() => verifyPassword('jhGGu78!', [], 0))
          .toThrowError("...")
      })
    })
    

    이 코드를 보면 함수의 인수들이 각자 무슨 의미인지 알겠는가. 0은 요일을 의미한다. []는 비밀번호 유효성을 검증하는 규칙의 배열이다. jhGGu78!은 비밀번호다. 아무 비밀번호. 그런데 정말 아무 비밀번호일지 알 수 있겠는가?

    이런 테스트는 결국 구현부를 까봐야 한다. 이렇게 수정해보자.

    유의미한 변수 사용

    describe("password verifier", () => {
      test("on weekends, throws exceptions", () => {
        const ANY_PASSWORD = 'anything';
        const SUNDAY = 0;
        const NO_RULES = [];
        expect(() => verifyPassword(ANY_PASSWORD, NO_RULES, SUNDAY))
          .toThrowError("...")
      })
    })
    
    • 변수의 이름과 값은 중요한 것을 설명하는 역할도 한다.
    • 하지만, 코드를 읽는 사람이 어떤 부분을 신경 쓰지 않아도 되는지 알려 주는 역할도 한다.

    저의 의견

    이 부분은 테스트 뿐만 아니라, 어떤 코드를 작성할 때에든 적용되는 원리인 것 같습니다. 테스트를 안 짜더라도 더 다짐하고 가보겠습니다.

    검증과 실행 단계 분리

    가독성을 높이려면 검증 단계와 실행 단계를 한 문장에 넣지 말라.

    // 좋지 않은 예시
    expect(verifier.verify("any value")[0]).toContain("fake reason");
    
    // 좋은 예시
    const result = verifier.verify("any value");
    expect(result[0]).toContain("fake reason");
    
    • 한 줄에 너무 많이 담지 마라.
    • 디버깅에도 더 좋다.

    초기화 및 설정 해제

    • 단위 테스트에서 초기화(setup)과 해제(teardown) 함수는 남용되기 쉽다.
      • 해제 작업의 가독성 감소(ex. 초기 설정 및 테스트 후 목 초기화 등)
      • 특히 초기화 함수에서 더 그렇다.

    beforeEach 함수에서 목 초기화

    • 설정 함수에서 목과 스텁을 설정하면 테스트 내에서는 해당 객체를 어디서 만들었는지 찾을 수 없다.
    • 테스트를 읽는 사람은 모의 객체의 사용 여부나 역할을 알지 못할 수 있다.
    • 초기화와 테스트 함수의 거리가 멀수록 더 그렇다.

    해결

    • 목은 테스트 내에서 직접 초기화하고 모든 기댓값을 설정한다.
    • 유지보수성을 위해 목 생성 팩토리 함수를 만든다.
    • beforeEach 같은 초기화 함수는 전혀 사용하지 않아도 된다.

    정리

    기억하고 싶은 좋은 말

    장의 초입에서 나온 이야기인데, 좋아서 적어봅니다.

    • 가독성은 테스트를 작성한 사람과 몇 달 후 이를 읽어야 하는 사람을 이어 주는 연결고리이다.
    • 테스트는 프로젝트에서 다음 세대 프로그래머에게 들려 주는 이야기이다.

    미래 세대와의 좋은 연결고리를 만들기 위해, 당장 테스트는 안 짜더라도 가독성 있는 코드를 짜봐야 겠네요.

    중요 내용 되새김

    • 변수의 이름과 값은, 코드를 읽는 사람이 어떤 부분을 신경 쓰지 않아도 되는지 알려 주는 역할도 한다.
    • 검증 단계와 실행 단계를 한 문장에 넣지 말라. 그냥 한 줄에 뭘 많이 넣지 말라.
    • 초기화 함수에서 목과 스텁 생성하지 말라. 테스트 자체에서 시작하라.
    • 유지보수성을 위해 목 생성 팩토리 함수를 만들어라.

    총평

    • 테스트를 작성하는 차원이 아니더라도 도움이 되는 내용이었어요.
    • 가독성과 유지보수성을 위한다면, 동작의 정의와 실행부의 거리가 멀면 안 된다는 걸 계속 강조하는 것 같아요. 초기화 함수와 테스트의 거리가 멀수록 추적이 어렵다고 하죠.
    • 그리고 저자의 팩토리 함수 사랑도 다시 알 수 있었어요. 그런데 그 효과가 이제는 조금 피부로 느껴지는 듯 해요. 저자의 외침이 닿는 듯 합니다.
    • 매직 넘버의 사용을 우아하게 해결하는 것도 좋았어요. 역시나 테스트가 아니더라도 안고 가야 할 기본 스킬들입니다.
    profile

    FE Developer 박승훈

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