FIRST 원칙

@023· October 20, 2025 · 4 min read

FIRST 원칙은 "Robert C. Martin in his book "Clean Code: A Handbook of Agile Software Craftsmanship"에서 소개되었다. 유명한 책이라 소지하고 있었고, 가끔 궁금한 부분을 펼쳐서 읽곤 했는데.. 이게 있었다는게 기억이 나지 않는 걸 보니 읽지 않았던 모양이다. 그래서 겸사겸사 꺼내서 읽어보았다.

F:Fast

유닛 테스트는 빨라야 한다. 보통 코드를 작성하고 테스트를 수시로 돌려본다. 그런데 유닛 테스트가 너무 느리다면 작업에 지장이 있을 수밖에 없다. 한 번 돌리는데 1분만 넘어가도 작업에 지장이 간다. 더 느려진다면 아마 테스트를 돌리는 횟수가 줄어들게 되고 초반에 문제를 찾지 못하게 될 수도 있다. 또, 유닛 테스트는 CI 프로세스에 포함된다. PR을 올리거나 push를 할 때, 배포를 할 때 CI 과정 중 하나로 보통 이 unit test가 동작하도록 되어있다. 그러므로, 유닛 테스트는 빨라야 한다.

I:Isolated

테스트는 다른 테스트에 종속적이면 안된다. 다른 테스트에 종속적인 테스트라면 테스트에 순서가 생긴다. 그리고 이는 테스트코드에 대한 추가적인 관리를 불러온다. 먼저 실행해야 할 테스트가 실패하면 다음 테스트도 실패하게 된다. 테스트는 다른 테스트에 종속성이 존재해서는 안된다. 아마 누구나 이해하는 원칙이라 생각한다

R:Repeatable

같은 테스트는 실행할 때마다 같은 결과를 만들어야 한다. 동일한 테스트코드는 어떤 환경에서든 결과가 같아야 한다. 테스트는 환경에 정속적이면 안된다.

S:Self-Validating

테스트는 스스로 결과물이 옳은 지 그른 지 판단할 수 있어야 한다. 테스트의 결과는 True or False, 성공 아니면 실패만 존재해야 한다. 즉, 테스트는 명확한 검증 조건(assert)을 사용해서 결과를 자동으로 판단할 수 있어야 한다는 의미이다.

예를 들면 아래와 같이 실제 함수가 호출된 경과를 확인하기 위해 log를 확인하거나 하는 것이 아니라,

def test_calculate_sum_bad_case():
    result = calculate_sum(2, 3)
    print("Result:", result)

아래와 같이 assert문을 이용하여 결과가 예상한 값과 동일한지 자동으로 평가하도록 해야 한다. 만약 결과와 다른 경우 AssertionError를 발생시켜 테스트가 실패하였다는 것을 알 수 있다.

def test_calculate_sum_good_case():
    result = calculate_sum(2, 3)
    assert result == 5

T:Timely/Thorough

유닛 테스트는 실제 코드가 구현하기 직전에 구현해야 한다. 이 원칙은 테스트 코드는 실제 코드보다 먼저 작성되어야 한다는 의미이다. TDD에 가까운 원칙이다. 책에서는 실제 코드를 작성하고 테스트코드를 만들면 실제 코드가 너무 복잡하거나, 테스트하기 어려운 구조인 경우엔 테스트코드 작성하기가 쉽지 않다는 예를 들고 있다. 나도 이런 경우가 종종 있긴 했었기 때문에 어떤 의미인지 이해되는 부분도 있다.

@023
focus and hustle