ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Jest] object에 대한 다양한 matcher 함수들
    Javascript, Typescript 2021. 12. 31. 14:07
    반응형

      facebook에서 개발 및 유지보수 중인 자바스크립트의 대표적인 테스팅 라이브러리인 Jest는 다양한 matcher함수를 제공한다.

    그중 두 오브젝트를 비교할 때 사용할 수 있는 다양한 matcher 함수 중 비슷하면서도 다른 toBe, toEqual, toStrictEqual, toMatchObject 함수의 동작과 그에 따른 차이점을 알아보자.

    toBe()

    기본형을 비교할 때 가장 많이 사용되는 toBe 함수는 두 값이 같은지 비교한다.

    비교 대상이 기본형(primitive type)이라면 두 원시 값이 같은지 비교하고, 오브젝트(object type)라면 같은 오브젝트에 대한 참조인지를 비교(Shallow compare)한다.

    두 값에 대해서 === 과는 다르게 동작하는 Object.is를 수행한다.(두 연산의 차이)

    • number에 대한 비교
    describe('toBe', () => {
      test('success', () => {
        const original = 1;
        expect(original).toBe(1); // <- success
      });
    
      test('fail', () => {
        const original = 1;
        expect(original).toBe(3); // <- fail
      });
    });
    • string에 대한 비교
    describe('toBe', () => {
      test('success', () => {
        const original = 'original';
        expect(original).toBe('original'); // <- success
      });
    
      test('fail', () => {
        const original = 'original';
        expect(original).toBe('different'); // <- fail
      });
    });
    • object에 대한 비교

    오브젝트에 대해서 얕은 비교를 하기 때문에 두 번째 테스트는 실패한다. 그리고 테스트 실패에 대한 이유도 알려준다.

    describe('toBe', () => {
      test('toBe', () => {
        const original = { name: "foo" };
        const compare = original;
        expect(original).toBe(compare); // <- success
      });
    
      test('toBe', () => {
        const original = { name: "foo" };
        const compare = { name: "foo" };
        expect(original).toBe(compare); // <- fail - serializes to the same string
      });
    });

    toEqual()

    toEqual 함수는 기본형인 경우 toBe와 같이 두 값이 같은지 비교하지만, 대상이 오브젝트인 경우 재귀적으로 프로퍼티를 비교하며 깊은 비교(Deep compare)를 수행한다.

    • object에 대한 비교
    describe('toEqual', () => {
      test('success', () => {
        const original = { name: 'foo' };
        const compare = { name: 'foo' };
        expect(original).toEqual(compare); // <- success
      });
    
      test('fail', () => {
        const original = { name: 'foo' };
        const compare = { name: 'bar' };
        expect(original).toEqual(compare); // <- fail
      });
    });

    original과 compare가 참조하는 오브젝트는 다르지만, 두 오브젝트 모두 name이라는 프로퍼티에 'foo'라는 값이 할당되어있기 때문에 테스트를 통과한다.

    주의할 점은 다음과 같이 추가적인 프로퍼티가 있는 경우 fail이 되지만, 추가적인 프로퍼티가 undefined라면 테스트를 통과한다.

    describe('toEqual', () => {
      test('fail', () => {
        const original = { name: 'foo' };
        const compare = { name: 'foo', age: 27 };
        expect(original).toEqual(compare); // <- fail
      });
    
      test('success', () => {
        const original = { name: 'foo' };
        const compare = { name: 'foo', age: undefined };
        expect(original).toEqual(compare); // <- success
      });
    });

    이같은 동작을 고려해서 toEqual함수를 사용할 수 있도록 하자.

    toStrictEqual()

    toStrictEqual은 오브젝트에 대해서 깊은 비교를 하는 것은 toEqual과 동일하지만, undefined인 프로퍼티까지 비교한다.

    따라서 toEqual을 사용할때는 통과하던 똑같은 테스트가 toStrictEqual을 사용하면 실패하게 된다.

    • object에 대한 비교
    describe('toStrictEqual', () => {
      test('fail', () => {
        const original = { name: 'foo' };
        const compare = { name: 'foo', age: undefined };
        expect(original).toStrictEqual(compare); // <- fail
      });
    });

    toStrictEqualtoEqual과 또 다른 중요한 점은 대상 오브젝트가 특정 클래스의 인스턴스인 경우, 인스턴스 타입까지 비교한다는 것이다.

    따라서 다음 테스트는 실패한다.

    class Original {
      constructor(name) {
        this.name = name;
      }
    }
    
    describe('toStrictEqual', () => {
      test('fail', () => {
        const original = new Original('foo');
        const compare = { name: 'foo' };
        expect(original).toStrictEqual(compare); // <- fail
      });
    });

    toMatchObject()

    toMatchObject함수는 비교할 오브젝트가 주어진 오브젝트의 부분집합(subset)인지를 비교한다.

    • object에 대한 비교
    describe('toMatchObject', () => {
      test('success', () => {
        const original = { name: 'foo', age: 27 };
        const compare = { name: 'foo', age: 27 };
        expect(original).toMatchObject(compare); // <- success
      });
    
      test('success', () => {
        const original = { name: 'foo', age: 27 };
        const compare = { name: 'foo' };
        expect(original).toMatchObject(compare); // <- success
      });
    
      test('fail', () => {
        const original = { name: 'foo' };
        const compare = { name: 'foo', age: 27 };
        expect(original).toMatchObject(compare); // <- fail
      });
    });

    toStrictEqual과 같이 undefined인 프로퍼티도 검사에 포함한다.

    describe('toMatchObject', () => {
      test('fail', () => {
        const original = { name: 'foo' };
        const compare = { name: 'foo', age: undefined };
        expect(original).toMatchObject(compare); // <- fail
      });
    });
    • 배열에 대한 비교

    배열에 대한 비교도 할 수 있는데, 배열의 원소가 모두 같은 경우에만 통과한다.(오브젝트가 있는 경우, 깊은 비교)

    describe('toMatchObject', () => {
      test('success', () => {
        const original = [1, 2, { name: 'foo' }];
        const compare = [1, 2, { name: 'foo' }];
        expect(original).toMatchObject(compare); // <- success
      });
    
      test('fail', () => {
        const original = [1, 2, 3];
        const compare = [1, 2];
        expect(original).toMatchObject(compare); // <- fail
      });
    });

    Conclusion

    • toBe: 같은 오브젝트에 대한 참조인지(얕은 비교)
    • toEqual: 같은 형태인지(깊은 비교), undefined 값 허용
    • toStrictEqual: 같은 형태 & 인스턴스인지, undefined 값 허용 X
    • toMatchObject: 부분집합인지, undefined 값 허용 X

    object에 대해서 비교를 하는 함수들이 여러 개 존재하고, 각각의 동작과 역할이 다 다르기 때문에 Jest의 비교 함수를 사용할 때는 각 함수의 동작과 사용방법에 대한 이해를 충분히 해서 좋은 테스트 코드를 작성할 수 있도록 해야겠다.

    반응형

    댓글

Designed by Tistory.