만든 것진행 중

검증

통과한 테스트가 무엇을 안 보고 있는지. 0이 두 가지를 뜻하면 그 테스트는 고장을 통과시킵니다.

배선 게이트

내가 온전히 소유한 유일한 QA 대상이다. 그래서 여기서는 테스트가 놓치는 것을 마음껏 실험할 수 있고, 실제로 여기서 나온 게 본업 쪽으로 넘어간다.

하네스 PASS는 작동 증명이 아니다

편집기 모드에서 도는 테스트 하네스는 빠르고 편하다. 그리고 런타임 배선 구멍을 통째로 가린다.

함수는 잘 돈다. 클래스도 맞다. 그런데 씬에서 그 참조가 비어 있으면 게임에서는 아무 일도 안 일어난다. 하네스는 그걸 못 본다 — 자기가 객체를 만들어서 테스트하기 때문이다.

실제로 이렇게 샌 적이 있다. 가드 코드가 참조가 있고 그게 활성이면 통과시킨다였는데, 씬에서 그 참조가 연결돼 있지 않았다. 그러면 조건이 항상 거짓이라 가드가 매번 스킵된다. 컴파일도 통과하고, 코드 리뷰도 통과하고, 런타임 예외도 안 난다. 조용히 안 도는 것뿐이다.

코드 리뷰는 .cs만 본다. 그래서 씬에 저장된 참조를 직접 읽는 게이트를 따로 뒀다. 코드가 맞느냐가 아니라 배선이 되어 있느냐를 묻는다. 특히 같은 참조를 한 컴포넌트엔 연결하고 다른 컴포넌트엔 빠뜨리는 부분 누락이 제일 잘 샌다.

0이 두 가지를 뜻하면 그 테스트는 고장을 통과시킨다

이 프로젝트에서 얻은 것 중 제일 값어치 있는 규칙이다.

파훼가 제대로 도는지 재려고 “막았을 때 피해가 0인가”를 테스트로 걸었다. 통과했다.

그런데 “막아서 피해 0”과 “사거리 밖이라 피해 0”이 구별되지 않는다. 방어 기능이 완전히 죽어 있어도, 어쩌다 사거리를 벗어나 있으면 그 테스트는 초록불이다.

드러난 경위가 재미있다. 오답 계열로 받았을 때는 피해가 60%여야 하는데, 그 케이스가 피해 0.0 (기대 13.2)으로 실패했다. 오답인데 0이 나왔다는 건 애초에 아무도 안 맞고 있었다는 뜻이고, 그 순간 정답 케이스의 통과가 거짓이었다는 게 같이 드러났다.

그래서 순서를 바꿨다. 방어를 재기 전에, 막지 않았을 때 실제로 맞는지를 먼저 세운다. 그 항목이 통과해야 방어 항목의 0이 의미를 갖는다.

격리한 이유를 주석이 아니라 테스트가 말하게 한다

무언가를 떼고 재는 일이 잦다. “방어막을 떼고 순수 피해를 잰다” 같은 것.

그럴 때 방어막이 원래 무엇을 하는지 검사하는 항목을 같이 둔다. 그 동작이 어느 날 사라지면 그 항목이 먼저 실패해서, 격리의 전제가 무너졌다는 걸 알려준다.

주석으로 “여기선 방어막을 뗐음”이라고 적어 두면 아무 일도 일어나지 않는다. 전제가 조용히 바뀌어도 주석은 그대로 남아 있기 때문이다.

끈 것이 원래 도는지도 검사한다

보스 패턴을 재려고 하네스를 세웠는데 자율 AI가 측정을 오염시켰다. 내가 주입한 공격이 끝나자마자 보스가 제 패턴으로 다음 공격을 시작해서 카운터 창이 다시 열렸다.

그래서 하네스 기본값을 자율 행동 정지로 바꿨다. 여기까지는 당연한 처방이다.

문제는 그다음이다. 꺼 놓기만 하면, 그 자율 행동이 원래 돌긴 하는지를 아무도 안 본다. 전부 통과하는데 실제 게임에서는 보스가 가만히 서 있는 상태를 놓친다.

그래서 “꺼 둔 그것이 원래 도는가”를 별도 항목으로 남긴다. 격리와 같은 논리인데, 이쪽이 더 자주 잊힌다.

상태가 아니라 결과로 판정한다

전조 타이밍을 잴 때 처음엔 상태 플래그가 언제 바뀌는지를 봤다. 틀렸다.

전조가 끝나도 공격이 안 나갈 수 있다. 사거리나 상태 검사에 걸려서 취소되는 경로가 있다. 플래그만 보면 “전조 끝났음”이 나오는데 플레이어는 안 맞는다.

그래서 판정 기준을 “플레이어가 언제 맞나”로 바꿨다. 내부 상태가 아니라 밖에서 관측되는 결과로 잰다. 같은 이유로 결함 종류마다 판정 방법을 갈라 뒀다 — 화면 결함은 찍어서 보고, 동작 결함은 실제로 돌려서 보고, 구조 결함만 정적으로 본다.

파일만 읽고 “고쳤다”고 보고하지 않는다. 고친 코드를 확인한 것과 고쳐진 게임을 확인한 것은 다르다.

실측했다는 사실이 측정 환경까지 보증하지는 않는다

마지막 하나가 제일 무섭다.

밸런스 상수 하나가 실측 기반이었다. “실제로 재서 넣은 값”이라 아무도 의심하지 않았다.

그런데 판정 경로에 결함이 있었고, 그 결함을 고치자 목표가 클리어 불가능해졌다. 재봤더니 결함이 있던 시절의 측정치가 지표를 3.5배 부풀리고 있었다 — 유령 판정이 킬 수를 올려놓은 것이다.

그래서 규칙을 하나 만들었다. 판정이나 피해 경로의 결함을 고치면, 그 경로에서 파생된 밸런스 상수를 전부 다시 잰다. 그리고 상수 옆에 재는 방법을 주석으로 남긴다.

“실측했다”는 말이 방패가 되면 안 된다. 측정은 측정 당시의 환경까지 같이 측정한다.

왜 이걸 여기서 하나

기법을 남의 프로젝트에서 시험하면 결과를 못 보여준다. 여기서는 보여줄 수 있다.

검증 방법 자체를 검증하는 자리로 쓰고 있다 — 이 하네스가 무엇을 못 보는가, 이 게이트를 통과했는데도 깨지는 경우가 있는가. 위의 “0이 두 가지를 뜻한다”와 “실측이 결함까지 보증하지 않는다”는 둘 다 여기서 나와서 본업 쪽 사고방식으로 넘어갔다.