๐Ÿ—’๏ธ Jest ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ์ •๋ฆฌ

๋ฆฌ๋ฒ„ riverยท2023๋…„ 11์›” 1์ผ
0
post-thumbnail

์šฐํ…Œ์ฝ” 6๊ธฐ ํ”„๋ฆฌ์ฝ”์Šค 2์ฃผ์ฐจ์— ์ ‘์–ด๋“ค๋ฉด์„œ
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์„ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์ธ Jest ๋ฌธ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค.

0. Jest ์„ค์น˜ ๋ฐ ์„ค์ •

0-1. ์„ค์น˜

npm init
npm install jest --save-dev

  • ์ถ”์ฒœ : vscode ์ฝ”๋“œ ์ž๋™ ์™„์„ฑ ๊ธฐ๋Šฅ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
npm install jest @types/jest --save-dev

0-2. ์„ค์ •

  • package.json ์ˆ˜์ •ํ•ด์ค€๋‹ค.
  "scripts": {
    "test": "jest"
  },

jest๋Š” test ํด๋” ์•ˆ์— ๋„ฃ์€ ํŒŒ์ผ๋“ค, .test.js ,.spec.jsํ˜•์‹์˜ ํŒŒ์ผ๋“ค์„ ์ž๋™์œผ๋กœ test ์ฝ”๋“œ๋กœ ์ธ์‹ํ•œ๋‹ค.



1. ํ…Œ์ŠคํŠธ ๊ธฐ๋ณธ ๊ตฌ์กฐ

1-1. ํ…Œ์ŠคํŠธ ๋ธ”๋ก

  • test ๋ฐ it: Jest์—์„œ๋Š” test์™€ it์ด ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋‘ ํ•จ์ˆ˜ ๋ชจ๋‘ ๋‹จ์ผ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ •์˜ํ•œ๋‹ค.

    // test ํ•จ์ˆ˜ ์‚ฌ์šฉ ์˜ˆ
    test('๋‘ ์ˆซ์ž์˜ ํ•ฉ', () => {
      expect(1 + 2).toBe(3);
    });
    
    // it ํ•จ์ˆ˜ ์‚ฌ์šฉ ์˜ˆ
    it('๊ฐ์ฒด ํ• ๋‹น ํ™•์ธ', () => {
      const obj = {};
      expect(obj).toEqual({});
    });

1-2. describe ๋ธ”๋ก

  • ๊ตฌ์กฐ: describe ๋ธ”๋ก์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ด€๋ จ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๊ทธ๋ฃนํ™”ํ•œ๋‹ค. ์ด๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋–ค ๋ชจ๋“ˆ์ด๋‚˜ ๊ธฐ๋Šฅ์— ๊ด€๋ จ๋˜์–ด ์žˆ๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค.

    // describe ๋ธ”๋ก ์‚ฌ์šฉ ์˜ˆ
    describe('String ๊ด€๋ จ ํ…Œ์ŠคํŠธ', () => {
      test('๋ฌธ์ž์—ด ํ•ฉ์น˜๊ธฐ', () => {
        expect('Hello' + 'World').toBe('HelloWorld');
      });
    
      it('๋ฌธ์ž์—ด ๊ธธ์ด', () => {
        expect('Hello'.length).toEqual(5);
      });
    });

1-3. before์™€ after ํ›…

  • ์‚ฌ์šฉ ๋ชฉ์ : ๊ณตํ†ต๋œ ์ค€๋น„ ์ž‘์—…์ด๋‚˜ ์ •๋ฆฌ ์ž‘์—…์„ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.
    - beforeEach: ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋œ๋‹ค.
    - afterEach: ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋œ ํ›„์— ์‹คํ–‰๋œ๋‹ค.
    - beforeAll: ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋œ๋‹ค.
    - afterAll: ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋œ ํ›„์— ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋œ๋‹ค.

    // before์™€ after ํ›… ์‚ฌ์šฉ ์˜ˆ
    describe('Array ๊ด€๋ จ ํ…Œ์ŠคํŠธ', () => {
      let array;
    
      beforeEach(() => {
        array = [1, 2, 3];
      });
    
      afterEach(() => {
        array = [];
      });
    
      test('๋ฐฐ์—ด ์š”์†Œ ์ถ”๊ฐ€', () => {
        array.push(4);
        expect(array).toEqual([1, 2, 3, 4]);
      });
    
      it('๋ฐฐ์—ด ์š”์†Œ ์ œ๊ฑฐ', () => {
        array.pop();
        expect(array).not.toContain(3);
      });
    });

1-4. ํ…Œ์ŠคํŠธ ํ•„ํ„ฐ๋ง

  • ํŠน์ • ํ…Œ์ŠคํŠธ ์‹คํ–‰: test.only ๋˜๋Š” it.only๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ํŠน์ • ํ…Œ์ŠคํŠธ ๋˜๋Š” ํ…Œ์ŠคํŠธ ๊ทธ๋ฃน๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.



2. ๋‹จ์–ธ (Assertions)

2-1. ๊ธฐ๋ณธ ๋‹จ์–ธ

  • toEqual vs toBe: toEqual์€ ๊ฐ์ฒด์˜ ๋‚ด์šฉ์ด ๊ฐ™์€์ง€ ํ™•์ธํ•˜๊ณ , toBe๋Š” ๊ฐ™์€ ๊ฐ์ฒด์ธ์ง€ (์ฆ‰, ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š”์ง€) ํ™•์ธํ•œ๋‹ค.

    test('๊ฐ์ฒด ๋‚ด์šฉ ๋น„๊ต', () => {
      const obj = { a: 1, b: 2 };
      expect(obj).toEqual({ a: 1, b: 2 });
      expect(obj).not.toBe({ a: 1, b: 2 }); // toBe๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊นŒ์ง€ ๋น„๊ตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹คํŒจ
    });
    

2-2. Truthiness

  • toBeNull, toBeUndefined, toBeTruthy, toBeFalsy:

    test('null ๊ฒ€์‚ฌ', () => {
      const n = null;
      expect(n).toBeNull();
      expect(n).toBeDefined();
      expect(n).not.toBeUndefined();
      expect(n).not.toBeTruthy();
      expect(n).toBeFalsy();
    });

2-3. ์ˆซ์ž ๊ด€๋ จ ๋‹จ์–ธ

  • toBeGreaterThan, toBeLessThan: ์ˆซ์ž ๋น„๊ต๋ฅผ ์œ„ํ•œ ๋‹จ์–ธ๋“ค์ด๋‹ค.

    test('์ˆซ์ž ๋น„๊ต', () => {
      const value = 2 + 2;
      expect(value).toBeGreaterThan(3);
      expect(value).toBeLessThan(5);
      expect(value).toBe(4);
      expect(value).toEqual(4);
    });

  • toBeCloseTo : ๋ถ€๋™ ์†Œ์ˆ˜์  ์ˆซ์ž๋“ค์„ ๋น„๊ตํ•œ๋‹ค.
    JavaScript์˜ ๋ถ€๋™ ์†Œ์ˆ˜์  ๊ณ„์‚ฐ ์˜ค์ฐจ ๋•Œ๋ฌธ์— ์œ ์šฉํ•˜๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด, 0.2 + 0.1์ด ์ •ํ™•ํžˆ 0.3์ด ์•„๋‹ ๋•Œ, ์ด ๋‹จ์–ธ์€ ๋‘ ์ˆซ์ž๊ฐ€ ์ถฉ๋ถ„ํžˆ ๊ฐ€๊นŒ์šด์ง€๋ฅผ ํŒ๋‹จํ•œ๋‹ค. ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์†Œ์ˆ˜์  ์•„๋ž˜ ์ž๋ฆฟ์ˆ˜๋ฅผ ์ง€์ •ํ•ด ๋น„๊ต ์ •๋ฐ€๋„๋ฅผ ์กฐ์ ˆํ•œ๋‹ค.

      test("adding floats", () => {
        const value = 0.1 + 0.2;
        expect(value).toBeCloseTo(0.3);
        expect(value).toBeCloseTo(0.29); // fail
        expect(value).toBeCloseTo(0.299); // pass
      });

  • toBeCloseTo : ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์†Œ์ˆ˜์  ์•„๋ž˜ ์ž๋ฆฟ์ˆ˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด, ๋น„๊ต์˜ ์ •๋ฐ€๋„๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‘ ์ˆซ์ž๊ฐ€ ์†Œ์ˆ˜์  ์•„๋ž˜ ๋‘ ์ž๋ฆฌ๊นŒ์ง€๋งŒ ๋น„๊ตํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.

    expect(0.223).toBeCloseTo(0.22, 2); // pass

2-4. ๋ฌธ์ž์—ด ๊ด€๋ จ ๋‹จ์–ธ

  • toMatch: ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•ด ๋ฌธ์ž์—ด์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

    test('๋ฌธ์ž์—ด์— ํŠน์ • ๋ฌธ์ž๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ', () => {
      expect('team').not.toMatch(/I/);
      expect('Christoph').toMatch(/stop/);
    });

2-5. ๋ฐฐ์—ด๊ณผ ๋ฐ˜๋ณต ๊ฐ€๋Šฅ ๊ฐ์ฒด

  • toContain: ๋ฐฐ์—ด์ด๋‚˜ ๋ฐ˜๋ณต ๊ฐ€๋Šฅ ๊ฐ์ฒด๊ฐ€ ํŠน์ • ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

    test('๋ฐฐ์—ด์— ํŠน์ • ์š”์†Œ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ', () => {
      const shoppingList = ['diapers', 'kleenex', 'trash bags', 'paper towels', 'milk'];
      expect(shoppingList).toContain('milk');
      expect(new Set(shoppingList)).toContain('milk');
    });

  • toContainEqual : ๋ฐฐ์—ด์ด ํŠน์ • ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์š”์†Œ๊ฐ€ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ์—๋„ ์ •ํ™•ํ•œ ์ผ์น˜ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค.

    // ๋ฐฐ์—ด ์•ˆ์— { name: "Bob" } ๊ฐ์ฒด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธ
    expect([{ name: "Alice" }, { name: "Bob" }]).toContainEqual({ name: "Bob" });

2-6. ์˜ˆ์™ธ

  • toThrow: ํ•จ์ˆ˜๊ฐ€ ํŠน์ • ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š”์ง€ ํ…Œ์ŠคํŠธํ•œ๋‹ค.

    function compileAndroidCode() {
      throw new Error('you are using the wrong JDK');
    }
    
    test('ํŠน์ • ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š”์ง€ ํ™•์ธ', () => {
      expect(() => compileAndroidCode()).toThrow();
      expect(() => compileAndroidCode()).toThrow(Error);
    
      // ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋‚˜ ์ •๊ทœ์‹์„ ์ด์šฉํ•œ ์˜ค๋ฅ˜ ๊ฒ€์‚ฌ
      expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
      expect(() => compileAndroidCode()).toThrow(/JDK/);
    });

3. ๋ชจ์˜ ํ•จ์ˆ˜ (Mock Functions)

3-1. ๋ชจ์˜ ํ•จ์ˆ˜ ์ƒ์„ฑ

  • jest.fn(): ๋ชจ์˜ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ˜ธ์ถœ ์ •๋ณด๋ฅผ ์ถ”์ ํ•œ๋‹ค.
    test('๋ชจ์˜ ํ•จ์ˆ˜ ์˜ˆ์ œ', () => {
      const mockFn = jest.fn();
      mockFn(1);
      expect(mockFn).toHaveBeenCalled();
      expect(mockFn).toHaveBeenCalledWith(1);
    });

3-2. ๋ชจ์˜ ํ•จ์ˆ˜ ์‚ฌ์šฉ

  • ํ•จ์ˆ˜ ๋ชจ์˜ : ์™ธ๋ถ€ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ์˜ํ–ฅ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด, ์‹ค์ œ ํ•จ์ˆ˜ ๋Œ€์‹  ๋ชจ์˜ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

    test('๋ชจ์˜ ๊ตฌํ˜„ ์˜ˆ์ œ', () => {
      const mockFn = jest.fn().mockImplementation(scalar => 42 + scalar);
      // ๋˜๋Š” jest.fn(scalar => 42 + scalar);
    
      expect(mockFn(1)).toBe(43);
    });
    

  • ๋ชจ์˜ ๊ตฌํ˜„ : mockImplementation()์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ์˜ ํ•จ์ˆ˜์˜ ๊ตฌ์ฒด์ ์ธ ๋™์ž‘์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

    test('๋ชจ์˜ ๊ตฌํ˜„ ์˜ˆ์ œ', () => {
      const mockFn = jest.fn().mockImplementation(scalar => 42 + scalar);
      // ๋˜๋Š” jest.fn(scalar => 42 + scalar);
    
      expect(mockFn(1)).toBe(43);
    });
    

3-3. ๋ชจ์˜ ํ•จ์ˆ˜ ๊ฒ€์ฆ

  • toHaveBeenCalled, toHaveBeenCalledTimes : ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€, ํŠน์ • ํšŸ์ˆ˜๋งŒํผ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

    test('๋ชจ์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์—ฌ๋ถ€ ๋ฐ ํšŸ์ˆ˜ ๊ฒ€์ฆ', () => {
      const mockFn = jest.fn();
      mockFn();
      mockFn([1, 2, 3]);
    
      // ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€
      expect(mockFn).toHaveBeenCalled();
    
      // ์ •ํ™•ํžˆ 2๋ฒˆ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€
      expect(mockFn).toHaveBeenCalledTimes(2);
    
      // ๋‘ ๋ฒˆ์งธ ํ˜ธ์ถœ ๋•Œ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ [1, 2, 3]์ด์—ˆ๋Š”์ง€
      expect(mockFn.mock.calls[1][0]).toEqual([1, 2, 3]);
    });


4. ๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ

4-1. Promise ํ…Œ์ŠคํŠธ

  • resolves, rejects: Promise๊ฐ€ ํ•ด๊ฒฐ๋˜๊ฑฐ๋‚˜ ๊ฑฐ๋ถ€๋  ๋•Œ์˜ ์ƒํƒœ๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค.

    test('Promise๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ํ•ด๊ฒฐ๋˜๋Š”์ง€ ํ™•์ธ', () => {
      return expect(Promise.resolve('์„ฑ๊ณต')).resolves.toBe('์„ฑ๊ณต');
    });
    
    test('Promise๊ฐ€ ์‹คํŒจํ•˜๋ฉฐ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š”์ง€ ํ™•์ธ', () => {
      return expect(Promise.reject(new Error('์‹คํŒจ'))).rejects.toThrow('์‹คํŒจ');
    });
    

4-2. async/await ์‚ฌ์šฉ

  • ๋น„๋™๊ธฐ ํ•จ์ˆ˜: async ํ•จ์ˆ˜ ๋‚ด์—์„œ await expect()์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค.

    test('async/await์„ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ', async () => {
      await expect(Promise.resolve('์„ฑ๊ณต')).resolves.toBe('์„ฑ๊ณต');
      await expect(Promise.reject(new Error('์‹คํŒจ'))).rejects.toThrow('์‹คํŒจ');
    });
    


๐Ÿ““ ์ •๋ณด ์ถœ์ฒ˜

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด