Skip to content

1. 테스트 가이드: 코드의 견고함을 지키는 방법

이 문서에서는 '달빛 약속' 프로젝트의 테스트를 실행하고, 새로운 테스트를 작성하는 방법에 대해 안내합니다.

테스트 실행하기

'달빛 약속' 프로젝트의 모든 테스트는 Deno의 내장 테스트 러너를 사용합니다. 프로젝트의 루트 디렉터리에서 다음 명령어를 실행하여 모든 테스트를 실행할 수 있습니다.

bash
deno task nested-test

이 명령어는 deno.json 파일에 정의된 nested-test 스크립트의 일부로, 워크스페이스에 정의된 모든 서브모듈의 test 태스크를 재귀적으로 실행합니다. 프로젝트 내의 모든 테스트 파일(일반적으로 *.test.ts 패턴을 따름)을 찾아 실행합니다. 테스트가 성공적으로 완료되면 ok 메시지를 볼 수 있습니다.

특정 테스트만 실행하기

만약 특정 테스트 파일이나 특정 테스트 케이스만 실행하고 싶다면, 다음과 같이 deno test 명령어를 직접 사용할 수 있습니다.

  • 특정 파일만 실행: deno test test/connect.test.ts
  • 특정 이름의 테스트만 실행: deno test --filter "특정 테스트 이름"

테스트 코드 살펴보기

'달빛 약속'의 테스트 코드는 주로 test/ 디렉터리 아래에 위치합니다. 각 테스트 파일은 특정 기능이나 모듈에 대한 테스트를 포함하고 있습니다.

  • /test/codes: 실제 .yak 코드 예시와 그 실행 결과를 테스트하는 파일들이 포함되어 있습니다. 이는 언어의 문법과 동작을 이해하는 데 매우 유용합니다.

테스트 코드를 읽어보면, 각 기능이 어떤 입력에 대해 어떤 출력을 기대하는지 명확하게 파악할 수 있습니다. 이는 새로운 기능을 구현하거나 버그를 수정할 때 큰 도움이 됩니다.

새로운 테스트 작성하기

새로운 기능을 추가하거나 버그를 수정했다면, 해당 변경 사항을 검증할 수 있는 테스트 코드를 작성하는 것이 좋습니다. '달빛 약속'에서는 특히 코드 실행 결과를 검증하는 테스트를 위해 다음과 같은 편리한 컨벤션을 제공합니다.

.yak.yak.out 파일을 이용한 테스트

test/codes/ 디렉터리 하위에는 실제 '달빛 약속' 코드(.yak 파일)와 그 코드의 예상 실행 결과(.yak.out 파일)가 쌍으로 존재합니다. 예를 들어, test/codes/apple_dividing.yak 파일의 실행 결과는 test/codes/apple_dividing.yak.out 파일에 정의되어 있습니다.

이러한 테스트는 /test/codes/index.test.ts 파일에 의해 자동으로 감지되고 실행됩니다. 따라서 새로운 '달빛 약속' 코드의 실행 결과를 테스트하고 싶다면, 다음 절차를 따르면 됩니다.

  1. test/codes/ 디렉터리 안에 새로운 .yak 파일을 생성하고 테스트할 '달빛 약속' 코드를 작성합니다.
  2. 동일한 파일 이름으로 .yak.out 파일을 생성하고, .yak 파일의 코드를 실행했을 때 보여주기 명령을 통해 출력될 정확한 예상 결과를 작성합니다. 각 보여주기 명령의 출력은 새로운 줄로 구분되어야 합니다.

예시:

test/codes/my_new_feature.yak

yak
숫자 = 10
만약 숫자 > 5 이면
    "숫자가 5보다 큽니다" 보여주기
아니면
    "숫자가 5보다 작거나 같습니다" 보여주기

test/codes/my_new_feature.yak.out

숫자가 5보다 큽니다

이렇게 파일을 생성한 후 deno task nested-test 명령어를 실행하면, 작성한 .yak 코드가 .yak.out에 정의된 결과와 일치하는지 자동으로 검증됩니다.

TypeScript를 이용한 고급 테스트

더 복잡한 시나리오나 내부 로직을 테스트해야 한다면, 기존 테스트 파일의 패턴을 참고하여 새로운 .test.ts 파일을 생성하고, Deno.test 함수를 사용하여 테스트 케이스를 정의할 수 있습니다.

typescript
Deno.test('Mentioning with variable in parameter', async () => {
    let output = ''

    const session = new YaksokSession({
        stdout(message) {
            output += message + '\n'
        },
    })
    session.addModule(
        '하랑봇',
        `약속, (내용) 말하기
    내용 + "? 어쩌라고." 보여주기

날씨 = "비"`,
    )

    session.addModule(
        'main',
        `할말 = "뭐라고"
@하랑봇 할말 말하기
@하랑봇 (@하랑봇 날씨) 말하기`,
    )

    await session.runModule('main')

    assertEquals(
        output,
        `뭐라고? 어쩌라고.
비? 어쩌라고.
`,
    )
})

테스트를 작성할 때는 다음을 고려해주세요.

  • 명확한 이름: 테스트의 목적을 명확히 알 수 있는 이름을 사용합니다.
  • 단일 책임: 하나의 테스트 케이스는 하나의 특정 기능이나 시나리오만 테스트하도록 합니다.
  • 예상 결과 명시: 어떤 입력에 대해 어떤 출력을 기대하는지 명확하게 명시합니다.

왜 테스트가 중요한가요?

  • 회귀 방지: 변경 사항이 이전에 잘 작동하던 기능을 망가뜨리는 '회귀(Regression)'를 방지합니다.
  • 기능 검증: 새로운 기능이 의도한 대로 정확하게 동작하는지 확인합니다.
  • 리팩토링의 안전망: 코드를 개선(리팩토링)할 때, 테스트가 있다면 변경 사항이 시스템에 부정적인 영향을 미치지 않는다는 확신을 가질 수 있습니다.
  • 실패하는 테스트의 중요성: 테스트는 단순히 성공하는 것만을 의미하지 않습니다. 때로는 실패하도록 의도된 테스트가 중요합니다. 예를 들어, 잘못된 문법이나 예상치 못한 입력에 대해 언어가 올바른 오류를 발생시키는지 확인하는 테스트, 또는 아직 구현되지 않은 기능에 대한 테스트는 의도적으로 실패해야만 합니다. 이러한 실패하는 테스트를 통해 우리는 언어의 경계를 정의하고, 에러 처리 메커니즘이 예상대로 동작하는지, 그리고 어떤 기능이 아직 개발 중인지 명확히 알 수 있습니다.