Jest, Enzyme 을 통한 리액트 컴포넌트 유닛 테스팅


이 튜토리얼에서는, 유닛 테스팅의 간단한 소개와, create-react-app 으로 만든 프로젝트에서 Jest 와 Enzyme 을 통하여 유닛 테스팅을 하는 방법에 대하여 다뤄보겠습니다.

Github 코드: https://github.com/vlpt-playground/react-test-tutorial

이 강의는 FastCampus 오프라인 강의 에서 사용된 자료이며 부연설명이 생략되어있습니다.

유닛 테스팅이란?

개발적인 측면에서, 유닛 테스팅이라는 개념이 익숙한 분들도 계실꺼고, 그렇지 않는 분들도 계실 겁니다.

유닛 테스팅이란, 소프트웨어를 기능별로 쪼개고, 그리고 그 기능 내부에서 사용되는 함수들도, 쪼개고 쪼개서 아주 작은 단위로 테스팅을 하는것을 의미합니다.

우리의 일상에 비유하여 “홍차 끓여 마시기” 를 테스트 하기 위해서, 전체적인 작업을 하나로 보면서 하는게 아니라 다음과 같은 흐름처럼:

  1. 물 끓이기
  2. 티포트와 찻잔에 뜨거운물을 부어 데우기
  3. 찻잎 놓기
  4. 차 우리기
  5. 찻잔에 차 따르기

작업을 하나 하나 나눠서, 각 작업이 잘 이뤄지는지 확인을 합니다.

우리가 만든 소프트웨어가 제대로 작동을 하는지 테스팅을 하기 위해서, 물론 우리가 직접 조작을 하면서 테스팅을 할 수도 있습니다. 하지만, 프로젝트가 커진다면, 매번 코드를 수정 / 새로 작성할때마다 모든 작업이 제대로 이뤄지는지 사람이 직접 확인을 한다면 매우 비효율적일 것입니다. 빠트리는 것도 있을 것이구요.

이러한 작업을, 사람이 아닌, 기계가 하도록 우리는 테스트 코드를 작성하여 진행 할 수도 있는데요, 이를 테스트 자동화라고 부릅니다.

어떠한 경우에 유용할까?

기존에 유닛 테스팅을 많이 한적이 없었다면, 정확히 이게 어떠한 상황에 유용할지 감이 잘 잡히지 않을 수도 있습니다.

우선, 여러분들이 프로젝트를 다른 사람들과 협업을 하게 되는 경우, 유닛 테스팅은 매우 강력한 역할을 하게 됩니다.

예를들어 여러분이 코드 A, B 를 작성했고, 팀원이 코드 C, D 를 구현했다고 가정해봅시다.

그리고, G 라는 기능을 구현하기 위하여, 코드 A 와 C 가 사용되었다고 가정을 해봅시다. G 기능을 구현하면서, 여러분이 여러분의 팀원이 작성한 코드 C 를 아주 조금 수정했습니다. 코드 C 가 잘 작동하는것을 확인했고, G도 잘 작동하는것을 확인 했는데요, 갑자기 의도치 않게 C 기능이 고장나버렸습니다.

만약에, 유닛 테스팅을 했더라면, C 기능이 고장나버린것을 코드를 작성하고 바로 발견 할 수 있지만, 유닛 테스팅을 하지 않을 경우에는, 어쩌다가 해당 버그를 발견하지 못 할 가능성도 있습니다.

간단한 이야기를 길게 설명했는데, 짧게 정리하자면 다음과 같습니다:

유닛 테스팅은, 내가 작성한 코드가 다른 코드들을 망가뜨리지 않도록, 적어도 우리가 사전에 정의한 상황속에서 보장해줍니다.

유닛 테스팅이 필요하지 않을때도 있다

유닛 테스팅은, 부가적인것이여서, 무조건 해야하는것은 아닙니다. 그리고, 가끔씩은, 아예 하지 않는편이 나을때도 있습니다. 예를들어서, 여러분이 만든 프로젝트가 혼자서 작업하는 것이고, 또 소규모 프로젝트라면, 유닛 테스팅을 하는것은 오히려 진행속도를 늦추게 될 수 있습니다. 하지만 물론, 비록 소규모 프로젝트일 지라도 나중에 커질 가능성이 있다면, 시작 단계부터 해두면 나중에 시간을 많이 아낄 수 있게 될 것입니다.

리액트 컴포넌트 테스팅

리액트 프로젝트 또한, 컴포넌트 단위로 하나하나 테스트 로직을 정해줄 수 있습니다. 리액트 컴포넌트를 테스팅할때는, 주로 다음과 같은 형식으로 하게 됩니다.

  1. 특정 props 에 따라 컴포넌트가 크래쉬 없이 잘 렌더링이 되는지 확인
  2. 이전에 렌더링했던 결과와, 지금 렌더링한 결과가 일치하는지 확인
  3. 특정 DOM 이벤트를 시뮬레이트 하여, 원하는 변화가 제대로 발생하는지 확인
  4. 렌더링된 결과물을 이미지 로 저장을 하여 픽셀을 하나하나 확인해서 모두 일치하는지 확인

이 포스트에선, 1~3번을 다루도록 하고, 4번의 경우엔 스토리북 을 통해서 하는 것이 효율적이고 편하므로, 나중에 다뤄보도록 하겠습니다.

자! 그러면 시작해봅시다!

프로젝트 생성, 코드 준비

우선, create-react-app 을 통하여 리액트 프로젝트를 생성하세요.

$ create-react-app react-test-tutorial

이미 알고 계실 수도 있겠지만, CRA 로 만든 리액트 프로젝트에는 테스트 환경이 이미 다 준비가 되어있고, 기본 테스트 파일 (App.test.js) 또한 존재 하지요.

한번 해당 파일을 확인해볼까요?

src/App.test.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});

해당 컴포넌트가, 크래쉬 없이 제대로 렌더링이 되었는지 확인을 해줍니다. 이걸 실행하려면, 터미널로 프로젝트의 디렉토리에서 yarn test 를 입력하시면 됩니다.

그러면, 다음과 같은 결과가 나타납니다:

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.672s, estimated 1s
Ran all test suites related to changed files.

Watch Usage
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press q to quit watch mode.
 › Press Enter to trigger a test run.

다 잘 됐다고 나타나지요.

자, 이제 우리는 아주 간단한 리액트 컴포넌트 3가지를 만들어보도록 하겠습니다.

카운터 만들기

단순히 state 에 있는 값을 + 버튼과 – 버튼을 통하여 변경하는 Counter 컴포넌트 입니다.

src/components/Counter.js

import React, { Component } from 'react';

class Counter extends Component {
  state = {
    value: 1
  }
  onIncrease = () => {
    this.setState(({value}) => ({ value: value + 1 }));
  }
  onDecrease = () => {
    this.setState(({value}) => ({ value: value - 1 }));
  }
  render() {
    const { value } = this.state;
    const { onIncrease, onDecrease } = this;
    return (
      <div>
        <h1>카운터</h1>
        <h2>{value}</h2>
        <button onClick={onIncrease}>+</button>
        <button onClick={onDecrease}>-</button>
      </div>
    );
  }
}

export default Counter;

그리고, 이 컴포넌트를 App 에서 렌더링하세요.

src/App.js

import React, { Component } from 'react';
import Counter from './components/Counter';

class App extends Component {
  render() {
    return (
      <div>
        <Counter />
      </div>
    );
  }
}

export default App;

그리고, yarn start 를 하시면 다음과 같이 나타날 것입니다.

NameForm, NameList 구현하기

이번에는, 이름을 NameForm에 입력해서 등록하면 NameList 에 나타나도록 컴포넌트들을 구현하겠습니다. input 의 상태값은 NameForm 에 넣고, NameList 에서 보여줄 이름 목록에 대한 상태값은 App 에서 NameList 로 props 를 통하여 전달해주도록 설정하겠습니다.

src/components/NameForm.js

import React, { Component } from 'react';

class NameForm extends Component {
  static defaultProps = {
    onSubmit: () => console.warn('onSubmit not defined'),
  }
  state = {
    name: ''
  }
  onChange = (e) => {
    this.setState({
      name: e.target.value
    });
  }
  onSubmit = (e) => {
    const { name } = this.state;
    const { onInsert } = this.props;
    // 이름을 추가하고, name 값 초기화
    onInsert(name);
    this.setState({
      name: ''
    });
    e.preventDefault(); // submit 하면 기본적으로는 페이지가 새로고쳐지게 되는데 이를 방지함
  }
  render() {
    const { onSubmit, onChange } = this;
    const { name } = this.state;
    return (
      <form onSubmit={onSubmit}>
        <label>이름</label>
        <input type="text" value={name} onChange={onChange} />
        <button type="submit">등록</button>
      </form>
    );
  }
}

export default NameForm;

src/components/NameList.js

import React, { Component } from 'react';

class NameList extends Component {
  static defaultProps = {
    names: []
  }

  renderList() {
    const { names } = this.props;
    const nameList = names.map(
      (name, i) => (<li key={i}>{name}</li>)
    );
    return nameList;
  }

  render() {
    return (
      <ul>
        { this.renderList() }
      </ul>
    );
  }
}

export default NameList;

자, 이제 우리가 방금 만든 컴포넌트 두개를 App 에서 렌더링해주겠습니다. 이 과정에서 App 에서 state 값과 onInsert 메소드를 추가해줄게요.

src/App.js

import React, { Component } from 'react';
import Counter from './components/Counter';
import NameForm from './components/NameForm';
import NameList from './components/NameList';

class App extends Component {
  state = {
    names: ['벨로퍼트', '김민준']
  }

  onInsert = (name) => {
    this.setState(({names}) => ({ names: names.concat(name) }));
  }

  render() {
    const { names } = this.state;
    const { onInsert } = this;

    return (
      <div>
        <Counter />
        <hr />
        <h1>이름 목록</h1>
        <NameForm onInsert={onInsert}/>
        <NameList names={names}/>
      </div>
    );
  }
}

export default App;

이러한 결과가 완성 됐나요? 일단 제대로 작동하는지 확인해보세요.

스냅샷 테스팅

스냅샷 테스팅은, 컴포넌트를 주어진 설정으로 렌더링하고, 그 결과물을 파일로 저장합니다. 그리고, 다음번에 테스팅을 진행하게 되었을때, 이전의 결과물과 일치하는지 확인합니다.

초기 렌더링 결과도 비교 할 수 있지만, 컴포넌트의 내부 메소드를 호출시키고, 다시 렌더링 시켜서 그 결과물도 스냅샷을 저장시켜서, 각 상황에 모두 이전에 렌더링했던 결과와 일치하는지 비교를 할 수 있습니다.

스냅샷 테스팅을 하기 위하여, 우선 react-test-renderer 를 설치해주어야 합니다.

$ yarn add --dev react-test-renderer

설치를 다 하셨다면, Counter.js 를 위한 테스트 코드를 작성해보겠습니다.

src/components/Counter.test.js

import React from 'react';
import renderer from 'react-test-renderer';
import Counter from './Counter';

describe('Counter', () => {
  let component = null;

  it('renders correctly', () => {
    component = renderer.create(<Counter />);
  });

  it('matches snapshot', () => {
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  })
});

테스트를 하게 될 때 주요 키워드는, 다음과 같습니다:

  • describe
  • it
  • expect

우리가 코드 테스팅 로직을 쪼개고 쪼갤때, 일단 가장 작은 단위는 it 입니다. 예를 들자면:

it('is working!', () => {
  expect(something).toBeTruthy();
})

it 내부에서는 expect 를 통하여 특정 값이 우리가 예상한 값이 나왔는지 확인을 할 수 있습니다. 해당 방법은 다양한데, Jest 매뉴얼 에서 다양한 함수들을 확인해 보실 수 있습니다.

그리고 여러개의 it 을 describe 안에 넣을 수 있게 되며, describe 안에는 또 여러개의 describe 를 넣을 수 있습니다.

describe('...', () => {
  describe('...', () => {
    it('...', () => { });
    it('...', () => { });
  });
  describe('...', () => {
    it('...', () => { });
    it('...', () => { });
  });
});

describe 와 it 에서 첫번째 파라미터는 작업의 설명을 넣어주게 되는데, describe 에서는 어떤 기능을 확인하는지, 그리고 it 부분에선 무엇을 검사해야 되는지에 대한 설명을 넣으시면 됩니다.

설명을 넣을때는, 주로 영어로 작성합니다. 하지만, 영어로 작성하는 것이 익숙하지 않다면, 다음과 같이 한글로 작성해도 무방합니다:

import React from 'react';
import renderer from 'react-test-renderer';
import Counter from './Counter';

describe('Counter', () => {
  let component = null;

  it('초기 렌더링이 문제없이 되야함', () => {
    component = renderer.create(<Counter />);
  });

  it('초기 렌더링 스냅샷 일치함', () => {
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  })
});

이 포스트에서는 영어로 테스트 케이스 이름을 영어로 작성하고, 영어가 익숙하지 않은 분들을 위하여 설명 부분을 주석으로 한국어로 작성하겠습니다.

테스트 코드를 저장하셨다면, 자동으로 test 가 다시 작동하여 (만약에 yarn test 를 껐다면 다시 실행시키세요) 스냅샷이 생성됩니다.

스냅샷은 src/components/__snapshots__ 경로에 저장됩니다. 해당 디렉토리 내부의 Counter.test.snap 파일을 확인해보세요.

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Counter matches snapshot 1`] = `
<div>
  <h1>
    카운터
  </h1>
  <h2>
    1
  </h2>
  <button
    onClick={[Function]}
  >
    +
  </button>
  <button
    onClick={[Function]}
  >
    -
  </button>
</div>
`;

렌더링된 결과물이 저장되었군요!

한번, Counter.js 컴포넌트에서 기존에 <h1>카운터</h1> 라고 적혀져 있던 것을 <h1>카운터!</h1> 이런식으로 느낌표를 붙여보세요.

그러면, yarn test 를 호출한 터미널을 보시면:

위와 같이 나타나게 됩니다. 스냅샷 비교가 실패했군요.

카운터 부분이 다르다고 오류가 뜨죠? 터미널 창에서 Enter 키를 누르면 테스트를 다시 실행 할 수 있으며, U 키를 누르면, 스냅샷을 업데이트 하여 현재 스냅샷을 최신으로 설정하여 오류가 더 이상 나타나지 않게 하도록 할 수 있습니다.

초기 렌더링 스냅샷이 제대로 작동하는것을 확인했다면, 아까 h1 부분에 넣은 느낌표를 다시 지우세요.

내부 메소드 호출 및 state 조회

react-test-render 를 하면 실제로 컴포넌트가 렌더링 되기 때문에, 컴포넌트의 state 와 메소드에도 접근 할 수 있습니다.

메소드를 실행 시켜서 state 를 업데이트 시키고, 리렌더링을 하여 변화에 따라 우리가 의도한 대로 렌더링이 되는지, 스냅샷을 통하여 비교해보겠습니다.

src/components/Counter.test.js

import React from 'react';
import renderer from 'react-test-renderer';
import Counter from './Counter';

describe('Counter', () => {
  let component = null;

  it('renders correctly', () => {
    component = renderer.create(<Counter />);
  });

  it('matches snapshot', () => {
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  })

  // increase 가 잘 되는지 확인
  it('increases correctly', () => {
    component.getInstance().onIncrease();
    expect(component.getInstance().state.value).toBe(2); // value 값이 2인지 확인
    const tree = component.toJSON(); // re-render
    expect(tree).toMatchSnapshot(); // 스냅샷 비교
  });

  // decrease 가 잘 되는지 확인
  it('decreases correctly', () => {
    component.getInstance().onDecrease();
    expect(component.getInstance().state.value).toBe(1); // value 값이 1인지 확인
    const tree = component.toJSON(); // re-render
    expect(tree).toMatchSnapshot(); // 스냅샷 비교
  });
});

자, 이제 스냅샷을 통한 테스팅에 대한 감을 잡으셨나요? 자, 그럼 나머지 컴포넌트들도, 초기 렌더링에 해당하는 부분만, 테스트 코드를 작성해보겠습니다.

src/components/NameList.test.js

import React from 'react';
import renderer from 'react-test-renderer';
import  NameList from './NameList';

describe('NameList', () => {
  let component = null;

  it('renders correctly', () => {
    component = renderer.create(<NameList names={["벨로퍼트", "김민준"]} />);
  });

  it('matches snapshot', () => {
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

src/components/NameForm.test.js

import React from 'react';
import renderer from 'react-test-renderer';
import NameForm from './NameForm';

describe('NameForm', () => {
  let component = null;

  it('renders correctly', () => {
    component = renderer.create(<NameForm />);
  });

  it('matches snapshot', () => {
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

그리고, App.test.js 또한 새로 작성해주세요.

src/App.test.js

import React from 'react';
import renderer from 'react-test-renderer';
import  App from './App';

describe('App', () => {
  let component = null;

  it('renders correctly', () => {
    component = renderer.create(<App />);
  });

  it('matches snapshot', () => {
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });
});

Enzyme 을 통한 DOM 시뮬레이션

Enzyme 은 airbnb 에서 만든 리액트 컴포넌트 테스팅 도구 입니다. 이 도구를 사용하면 더욱 세밀한 리액트 컴포넌트 테스팅을 할 수 있게 됩니다.

Enzyme 을 통해서 DOM 이벤트를 시뮬레이트 할 수도 있고 (예: 버튼 클릭, 인풋 수정, 폼 등록 등), 모든 라이프사이클이 문제없이 돌아가는지도 확인 할 수 있습니다.

설치 및 적용

Enzyme 을 사용하려면, 설치를 먼저 해줘야겠죠?

$ yarn add enzyme enzyme-adapter-react-16

그 다음엔 src 디렉토리에 setupTests.js 라는 파일을 생성해주겠습니다. – 이 파일은 CRA 로 만든 프로젝트에서 필요한 테스트 설정입니다.

src/setupTests.js

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

이 코드를 작성하고 나서, 기존에 실행중이던 yarn test 를 종료하고 새로 실행해주세요.

NameForm 테스트 코드 재작성

기존의 NameForm 테스트 코드에서 작성하던 react-test-renderer 대신에, Enzyme 을 사용해보겠습니다.

import React from 'react';
import { shallow } from 'enzyme';
import NameForm from './NameForm';

describe('NameForm', () => {
  let component = null;

  it('renders correctly', () => {
    component = shallow(<NameForm />);
  });

  it('matches snapshot', () => {
    expect(component).toMatchSnapshot();
  });
});

이렇게 하고, 터미널 상에서는 U 를 눌러서 스냅샷을 업데이트하세요.

Enzyme 을 사용한 기본 스냅샷은, 가독성이 좋지 않습니다.

ShallowWrapper {
  "length": 1,
  Symbol(enzyme.__root__): [Circular],
  Symbol(enzyme.__unrendered__): <NameForm
    onSubmit={[Function]}
/>,
  Symbol(enzyme.__renderer__): Object {
    "batchedUpdates": [Function],
    "getNode": [Function],
    "render": [Function],

이런 모양이죠.

우리가 아까전에 설치한 enzyme-to-json 를 적용하면, 이전처럼 깔끔하게 저장 할 수 있습니다. package.json 에서 jest 값을 추가하세요.

package.json

{
  "name": "react-test-tutorial",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-scripts": "1.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {
    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1",
    "enzyme-to-json": "^3.3.1",
    "react-test-renderer": "^16.2.0"
  },
  "jest": {
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ]
  }
}

이를 저장하고 난 다음엔, yarn test 를 새로 시작해야 적용이 됩니다.

DOM 시뮬레이션 해보기

자, 이제는 우리가 기존엔 할 수 없던, DOM 이벤트를 시뮬레이트 해보겠습니다. 우선, 결과물에 form 과 input 이 있는지 확인을 해보겠습니다.

src/components/NameForm.test.js

import React from 'react';
import { shallow } from 'enzyme';
import NameForm from './NameForm';

describe('NameForm', () => {
  let component = null;

  it('renders correctly', () => {
    component = shallow(<NameForm />);
  });

  it('matches snapshot', () => {
    expect(component).toMatchSnapshot();
  });

  describe('insert new text', () => {
    it('has a form', () => {
      expect(component.find('form').exists()).toBe(true);
    })
    it('has an input', () => {
      expect(component.find('input').exists()).toBe(true);
    })
  })
});

렌더링을 하고나면 selector 를 통하여 특정 DOM 을 선택 할 수 있습니다. 선택 방식은 css, prop 값, 컴포넌트, 태그명이 있습니다.

자, 이제 DOM 을 선택했으니 이벤트를 시뮬레이트 해봅시다.

src/components/NameForm.test.js

import React from 'react';
import { shallow } from 'enzyme';
import NameForm from './NameForm';

describe('NameForm', () => {
  let component = null;

  // 테스트용 onInsert 함수. changed 값을 바꿔줌
  let changed = null;
  const onInsert = (name) => {
    changed = name;
  }

  it('renders correctly', () => {
    component = shallow(<NameForm onInsert={onInsert} />);
  });

  it('matches snapshot', () => {
    expect(component).toMatchSnapshot();
  });

  describe('insert new text', () => {
    it('has a form', () => {
      expect(component.find('form').exists()).toBe(true);
    });
    it('has an input', () => {
      expect(component.find('input').exists()).toBe(true);
    });
    it('simulates input change', () => {
      const mockedEvent = {
        target: {
          value: 'hello'
        }
      };
      // 이벤트를 시뮬레이트 합니다. 두번째 파라미터는 이벤트 객체입니다.
      component.find('input').simulate('change', mockedEvent); 
      expect(component.state().name).toBe('hello');
    });
    it('simulates form submit', () => {
      const mockedEvent = {
        preventDefault: () => null // onSubmit 에서 preventDefault 를 호출하게 되므로, 가짜 함수 추가
      };
      component.find('form').simulate('submit', mockedEvent);
      expect(component.state().name).toBe(''); // 등록 하면 값이 공백으로 변하며
      expect(changed).toBe('hello');
    })
  })
});

자, 이제 모든게 작동했는지 확인해보세요! Enzyme 을 사용하면 위 처럼 특정 DOM 에 이벤트를 시뮬레이트 할 수 도 있지만, 특정 DOM 에 특정 문자열 이 들어있는지, 혹은 특정 props가 설정 되었는지, 등 정말 수많은 기능 들로 리액트 컴포넌트 테스팅을 할 수 있으니, 매뉴얼을 참고해보시길 바랍니다.

정리

리액트 컴포넌트 테스팅의 기초중의 기초를 다뤄보셨습니다. 이번 튜토리얼에서 진행한 것 외에도, 리덕스 관련 테스팅, 이미지 비교 등이 남아있습니다. 테스팅은, 결고 간단한 주제는 아닙니다. 하지만, 꾸준히 조금씩 사용하다보면, 나중에 중요한 프로젝트에 적용하게 되었을 때 큰 이득을 누릴 수 있습니다.

Reference

  • 똥광이

    스냅샷을 깔끔하게 보여주기위해서 “enzyme-to-json 사용하기” 할때 별도로 “yarn add enzyme-to-json”가 필요한것 같습니다. 이전에 “설치하신” 이라고 언급이 되어 있긴한데 글에서는 해당 모듈설치를 표현한 부분이 없어서 신나서 따라하다가 빨간게 떠서 놀랐네요! 좋은글 감사합니다!

  • Chang Moo Moon

    안녕하세요. 스토리북 편도 기다리고 있겠습니다!

  • Ethan Choi

    안녕하세요 . 연재해주시는 강의보고 정말 많이 배워갑니다 ㅎㅎㅎ 궁금한게 있어서 그러는데요 ㅎㅎ src 폴더에 테스트 코드를 남기게 되면 나중에 배포파일을 만들때 소스코드 용량이 많아지지 않나요? ㅎㅎ 아니면 src와 같은레벨에 test 폴더를 만들어서 따로 관리할 경우에는 경로 설정을 어떻게 할수 있을까요? ㅎㅎㅎ

  • chul kim

    test 파일 상단에 import ‘../setupTests’ 를 명시해줘야된다는 것도 적어주셨으면 좋았을 것 같습니다..
    그리고 setupTests.js 파일 내에 {configure} 를 import 하는 대신에 Enzyme 을 import 하고 난 뒤에 Enzyme.configure({…}) 으로 해줘야 에러가 안납니다

  • 이전에 댓글을 달았는지 잘 모르겠지만.. 정말 여러 번 놀라고 많이 배우고 갑니다.
    감사합니다 🙂

  • dongdonggri

    이렇게 좋은 내용을 항상 정리하셔서 공유하셔서 정말 감사하고 , 존경스럽습니다.