본문 바로가기

프로젝트 개발일지/toss-slash 라이브러리

[toss-slash 라이브러리] assert 함수와 테스트 코드

이번에는 토스 슬래시 라이브러리에 있는 assert 함수를 이해해보려 한다.

근데 진짜 느끼는 거지만 토스 슬래시 라이브러리 보면 타입스크립트를 너무 잘 쓴다.

이번에도 타입 코드 부분들 위주로 알아보도록 하자.

 

일단 아래 코드는 assert 함수이다.

export const assert = (
  condition: unknown,
  error: string | Error = new Error()
): asserts condition => {
  if (!condition) {
    if (typeof error === "string") {
      throw new Error(error);
    } else {
      throw error;
    }
  }
};

 

코드에 대해서 간단히 설명하자면, 인자로 받는 condition에 만족했으면 하는 조건을 적어주고, 해당 조건이 만족하지 않으면 에러를 발생시키도록 한다.

이때 에러를 명시적으로 적어주면 해당 에러를 발생시키고, 그렇지 않다면 기본 에러를 발생시킨다.

 

여기서 내가 주목했던 점은 asserts 키워드였다.

이 assert 함수에서는 asserts 키워드를 사용한 타입 가드를 이용했다.

 

나는 지금까지 asserts 키워드를 사용해서 타입 가드 코드를 작성해본 적이 없었다.

그래서 가장 궁금했던 점은 아래 두 코드가 어떤 점이 차이가 있나였다.

function example<T>(value: T): value is number {
  return typeof value === 'number';
}
function example<T>(value: T): asserts value is number {
  if (typeof value !== 'number') {
    throw new Error('Value is not a number');
  }
}

 

두 코드 모두 타입 가드를 사용해서 타입을 제한하고 있다.

하지만 두 번째 코드처럼 asserts 키워드를 사용하면 첫 번째 코드의 타입 가드와는 달라진다.

 

어쨌든 타입 정보는 컴파일 타임에 제공되는 정보이지만, asserts 키워드는 런타임에서의 동작을 제한한다.

asserts 키워드를 사용해서 타입 가드를 수행하게 되면 런타임에 타입 가드 역할을 수행할 수 있게 된다.

그래서, asserts 키워드를 추가하면 런타임에 타입 정보를 확신시켜준다고 보면 된다.

위 assert 함수는 조건이 만족되지 않으면 런타임에 에러를 발생시키게 된다.

 

그래서 이제 assert 함수의 함수 시그니처를 한 번 보자.

const assert = (
  condition: unknown,
  error: string | Error = new Error()
): asserts condition

반환 타입으로 asserts condition이라고 적혀있다.

이는 런타임에 condition이 true일 때만을 보장해주는 용도라고 생각하면 된다.

함수 내용을 보면 알다시피, condition이 false인 경우에는 에러를 발생시키도록 했다.

 

그 다음으로는 테스트 코드이다.

사실 이 함수에서 테스트할 부분은 세 경우뿐이다.

1. 조건을 만족하지 못한 경우 + 기본 에러 발생

2. 조건을 만족하지 못한 경우 + 인자로 전달된 에러 메시지로 구성된 에러 발생

3. 조건을 만족한 경우

 

코드는 아주 간단하니 별로 설명할 부분은 없는 것 같아 코드만 올리고 마무리하도록 하겠다.

describe("assert", () => {
  it("condition이 만족되지 않은 경우 에러를 throw해야 합니다.", () => {
    const value: string = "string";

    expect(() => assert(value === "string1")).toThrowError(Error);
  });

  it("condition이 만족되지 않은 경우, 인자로 전달된 에러 메시지로 구성된 에러를 throw해야 합니다.", () => {
    const value = false;

    expect(() => assert(value, "error message")).toThrowError(
      new Error("error message")
    );
  });

  it("condition이 만족된 경우 에러를 throw하지 않아야 합니다.", () => {
    const value = true;

    expect(() => assert(value)).not.toThrowError();
  });
});