통과하는 테스트와 검사하는 테스트는 다르다

손으로 맞춰야 하는 목록 두 개

이 사이트에 정적 페이지를 하나 추가하려면 파일만 만들어서는 안 된다. 두 곳을 함께 고쳐야 한다.

하나는 빌드의 배포 화이트리스트다. 여기 없는 파일은 배포 산출물에 복사되지 않는다. 다른 하나는 사이트맵 생성 코드의 고정 경로 목록이다. 여기 없으면 사이트맵에 실리지 않는다.

빠뜨리면 어떻게 되는가. 파일은 레포에 멀쩡히 있고, 빌드는 성공하고, 테스트도 통과한다. 그런데 그 페이지는 세상에 없다. 조용히 빠진다.

개발 기록은 이 함정을 구조로 없앴다. 원고 파일 집합에서 사이트맵을 생성하니 목록을 손으로 맞출 일이 아예 없다. 그런데 정적 페이지에는 그대로 남아 있었다.

가드를 만들었다

새 페이지 두 장을 추가하기 전에 가드부터 만들었다. 세 곳이 일치하는지 본다.

it('루트의 모든 html이 배포 화이트리스트에 있다', () => {
  const entries = deployEntries();
  expect(files.filter((f) => !entries.includes(f))).toEqual([]);
});

it('루트의 모든 html이 sitemap 고정 경로에 있다', () => {
  const expected = files.map((f) =>
    f === 'index.html' ? 'https://leva.ai.kr/' : `https://leva.ai.kr/${f.replace(/\.html$/, '')}`,
  );
  expect(expected.filter((u) => !locs.includes(u))).toEqual([]);
});

돌렸더니 통과했다. 4건 전부 초록색이었다.

여기서 멈추면 안 된다

통과했다는 것은 두 가지 중 하나를 뜻한다. 검사했는데 문제가 없거나, 아무것도 검사하지 않았거나.

초록색만 봐서는 구별할 수 없다. 그래서 일부러 깨뜨려 봤다. 배포 목록에서 기존 페이지 하나를 지웠다.

× 정적 페이지 3자 일치 > 루트의 모든 html이 배포 화이트리스트에 있다
  → expected [ 'privacy.html' ] to deeply equal []

정확히 그 파일을 지목하며 실패했다. 이제 이 가드가 무엇을 하는지 안다. 확인하고 되돌린 다음, git diff가 비어 있는 것까지 봤다.

여기 걸린 시간은 1분이 안 된다.

왜 이 확인을 습관으로 만들었나

이 레포에서 아무것도 검사하지 않는 테스트가 통과로 위장한 적이 여러 번 있다.

주차별 진행률 차트가 신규 사용자에게 빈 상자로 보인 적이 있다. 값이 전부 0이라 막대 높이가 0이었는데, 테스트는 그 0을 정확히 기대하고 통과했다. 화면 코드와 테스트가 같은 계산식을 두 번 적어 둔 형태라, 식이 통째로 틀려도 둘이 똑같이 틀렸다.

텍스트 줄임 처리를 검증하는 테스트가 줄임이 일어나지 않는 짧은 문자열을 쓰고 있던 적도 있다. 결함을 알면서 그 조건을 피해 가는 입력으로 계속 초록색이었다.

공통점이 있다. 전부 통과하고 있었고, 아무도 그 통과가 무엇을 뜻하는지 확인하지 않았다.

같은 날 한 번 더 쓰였다

새 페이지에 이런 가드를 넣었다. 확정되지 않은 기간을 약속하지 말자는 규칙이다.

expect(html).not.toMatch(/며칠|영업일|\d+일 (안에|이내)/);

원고를 쓰고 돌렸더니 걸렸다. 그런데 걸린 문장은 초대 기간과 아무 상관이 없었다.

같은 길을 조금 앞서 지나간 사람이 남긴 답 하나가 며칠을 줄이기도 합니다.

정규식이 낱말만 보고 문맥을 구별하지 못한 것이다. 이건 테스트가 옳고 원고가 틀린 경우가 아니라, 테스트가 의도를 넘어 잡은 경우다. 그래서 기간을 약속하는 형태만 막도록 좁혔다.

expect(html).not.toMatch(/(며칠|영업일|\d+\s*일)\s*(안에|이내|내로)/);

여기서 다시 같은 질문이 생긴다. 좁힌 다음에도 원래 잡으려던 것을 잡는가? 가드를 약화시켜 놓고 통과만 확인하면, 방금 만든 것은 아무것도 막지 않는 장식이다.

그래서 표현들을 직접 넣어 봤다.

차단 : 며칠 안에 초대드립니다
차단 : 3일 이내에 승인됩니다
차단 : 영업일 기준 5일 이내
차단 : 2일 내로 연락드립니다
통과 : 며칠을 줄이기도 합니다
통과 : 신청하신 순서대로 순차 승인합니다
통과 : 정확한 기한을 약속드리지는 않겠습니다

막을 것은 막고 통과시킬 것은 통과시킨다. 이제 좁힌 게 맞다고 말할 근거가 있다.

규칙 하나

새로 만든 가드는 한 번은 실패시켜 본다. 실패하는 것을 보지 않았다면 그 가드가 무엇을 지키는지 모르는 것이고, 모르는 것을 지키고 있다고 믿는 편이 아예 없는 것보다 위험하다. 없으면 최소한 확인은 하니까.

← 개발 기록 목록으로