Test Code

`toBe`,`toEqual`과 `toStrictEqual`의 차이점

남희정 2023. 8. 10. 21:59

banilla JavaScript / pnpm + Vite + Vitest + v8(coverage)

 

이야기가 길어질 수 있으니 차이점만 궁금한 사람은 🔽 아래로

나는 JavaScript 기초 강의를 들으면서 배운대로 테스트 코드도 같이 짜보며 차근차근 공부하고 있어서 모르는 게 참 많다.

그만큼 나중에 스스로 기억하기 쉽고, 나와 같이 비슷하게 제로베이스로 시작하는 모두에게 도움을 주고자 내가 겪은 문제와 그걸 해결한 방법 그리고 왜 문제가 있었는지를 전부 적어볼 예정이다.

Vitest는 Jest와 호환이 매우 잘 되지만, 최대한 Vitest에서 일어나는 error로 생각하고 해결하기 때문에 절차가 다를 수 있으니 참고 바란다. 처음 겪는 error들을 보여주기 때문에 완벽하지 않을 수 있다. 성장 과정으로 봐주었으면 좋겠다!

 

error가 뜬 테스트는 굉장히 간단했다. 내장 함수 split을 써서 배열로 반환했고, console.log()로는 쉽게 끝냈고 테스트 코드를 작성했다.

 

`built-inQuiz0.js`

// 2. 사용자들의 id를 잘라내어 각각의 id를 배열로 보관
const ids = "user1, user2, user3, user4";

export { ids };

`built-inQuiz0.test.js`

import { test, expect } from "vitest";
import { ids } from "../built-inQuiz0";

test("각각의 id를 배열로 보관하기", () => {
  expect(ids.split(", ")).toBe(["user1", "user2", "user3", "user4"]);
});

언뜻 보기엔 뭐가 다른 거냐고 생각할 수 있다. (내가 그랬다.)

친절하게 deep Equality를 위해선 `toBe()`대신 `toStrictEqual()`을 쓰라는 메세지 덕분에 수정 후 pass될 수 있었다.

항상 `expect().toBe()`만으로 테스트를 했던 나는 호기심이 생겼다! 

 

`toBe`,`toEqual`과 `toStrictEqual`

Vitest의 공식 홈페이지를 많이 참고했다.

 

toBe

`toBe`는 기본 데이터(primitive)가 동일한지 또는 객체가 같은 참조(Reference)를 공유하는지를 확인하는 데 사용된다!

`expect(Object.is(3, 3)).toBe(true)`를 호출한 것이랑 동등하다.

객체가 동일하지 않지만 구조가 동일한지 확인하려면 `toEqual`을 사용할 수 있다.

 

import { expect, test } from 'vitest'

const stock = {
  type: 'apples',
  count: 13,
}

test('stock has 13 apples', () => {
  expect(stock.type).toBe('apples')
  expect(stock.count).toBe(13)
})

test('stocks are the same', () => {
  const refStock = stock // same reference

  expect(stock).toBe(refStock)
})

 

JavaScript에서 부동 소수점 연산은 정확한 값을 보장하지 않을 수 있기 때문에, 부동 소수점 수의 비교를 정확하게 하기 위해서는 `toBeCloseTo`를 사용하는 것이 좋다고도 안내되어있다.

 

toEqual

`toEqual`은 실제 값이 받은 값과 같거나, 만약 객체인 경우에는 구조가 동일한지를 확인하는 단언문이다.

두 객체의 구조와 값만을 비교하며, 객체의 타입은 신경쓰지 않는다.

재귀적으로 비교된다.

 

재귀적으로 비교된다?

객체의 속성들을 깊이 있는 구조까지 비교한다는 의미. 객체가 다른 객체를 포함하고 있을 때, 그 내부의 객체들도 `toEqual`로 비교 대상이 된다.

 

import { expect, test } from 'vitest';

const stockBill = {
  type: 'apples',
  count: 13,
};

const stockMary = {
  type: 'apples',
  count: 13,
};

test('stocks have the same properties', () => {
  expect(stockBill).toEqual(stockMary);
});

test('stocks are not the same', () => {
  expect(stockBill).not.toBe(stockMary);
});

 

toStrictEqual

`toStrictEqual`은 실제 값이 받은 값과 동일하거나, 객체인 경우 동일한 구조를 가지며 (재귀적으로 비교), 두 값이 동일한 타입인지 확인하는 단언문이다. 

 

`toEqual`과의 차이점

조금 더 엄격한 Equal이라고 보면 된다.

1. `undefined`속성을 가진 키는 확인된다. 예를 들어, `{a: undefined, b: 2}`는 `.toStrictEqual`을 사용할 때 {b: 2}와 일치하지 않는다.

2. 배열 내의 공백(사라진) 요소가 확인된다. 예를 들어, [, 1]는 `.toStrictEqual`을 사용할 때 [undefined, 1]와 일치하지 않는다.

3. 객체의 타입까지 확인하며, 클래스 인스턴스와 리터럴 객체를 구별한다. 예를 들어, ab라는 필드를 가진 클래스 인스턴스는 ab 필드를 가진 리터럴 객체와 일치하지 않는다.

 

import { expect, test } from 'vitest'

class Stock {
  constructor(type) {
    this.type = type
  }
}

test('structurally the same, but semantically different', () => {
  expect(new Stock('apples')).toEqual({ type: 'apples' })
  expect(new Stock('apples')).not.toStrictEqual({ type: 'apples' })
})

 

결론

내가 기존에 작성한 `toBe`는 참조값과 원시타입을 체크하기 때문에 오류가 떴던 것이다.

`toEqual`을 쓴 경우에도 pass가 되지만 엄격한 비교를 위해선 `toStrictEqual`을 습관화하는 것이 좋겠다. 

이걸 통해서 코어 자바스크립트 데이터 타입 부분이 생각난다 🤔

객체나 배열을 비교할 땐 `toBe`는 지양해야겠다.

 

 


[Vitest 공식 홈페이지]

ChatGPT🤖