react로 tdd 쵸큼 맛보기

Post on 17-Mar-2018

1.503 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

React로�TDD�쵸큼�맛보기김훈민@네이버

나는자바스크립트개발자다

김코딩

스마트에디터�3.0,�프론트엔드�개발�-�@네이버�

기술�블로그(huns.me)에�아주�가끔�글�쓰기�

페이스북�커뮤니티�프론트엔드개발그룹�운영하는�척�

네이버�사내�리액트�미트업�운영하는�척�

Facebook은�React를�왜�만들었을까�-�@FBDG�2015�

Angular2�vs�React,�React�vs�Angular2�-�@Deview�2016

오늘의�주제는�…

단위�테스트�어떻게�작성하나요?�

TDD,�왜�해요?�

TDD�하면�뭐가�좋아요?�

TDD�하면�설계를�잘�할�수�있나요?�

TDD�하면�클린�코드를�작성할�수�있나요?�

TDD�하는�모습이�궁금해요

오늘의�주제는�…

단위�테스트�어떻게�작성하나요?�

TDD,�왜�해요?�

TDD�하면�뭐가�좋아요?�

TDD�하면�설계를�잘�할�수�있나요?�

TDD�하면�클린�코드를�작성할�수�있나요?�

TDD�하는�모습이�궁금해요

오늘�만들�녀석은,�

“막막”�간단한�Spinbox

기본값은�200�

값�입력�

증가�버튼을�클릭하여�값이�1�증가�

감소�버튼을�클릭하면�값이�1�감소

환경은…?

Webpack�

ES2015�with�babel�

Jest�

Enzyme

들어가기�전에

테스트�설정은�잘�되어있나?

구현�코드 테스트�코드

it('스핀박스�생성할�수�있다.', () => { // given - 어떤�조건�또는�상황일�때 // when - 어떤�행위를�하면 // then - 결과가�무엇이어야�한다});

it('스핀박스를�생성할�수�있다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then expect(spinbox).toBeDefined();});

it('스핀박스�생성할�수�있다.', () => { // setup // exercise // verify});

“어디에서�시작하지?”

기본값은�200�

값�입력�

증가�버튼을�클릭하여�값이�1�증가�

감소�버튼을�클릭하면�값이�1�감소

“어디에서�시작하지?”

기본값은�200�

값�입력�

증가�버튼을�클릭하여�값이�1�증가�

감소�버튼을�클릭하면�값이�1�감소

it('스핀박스를�생성할�수�있다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then expect(spinbox).toBeDefined();});

it('스핀박스를�생성할�수�있다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then expect(spinbox).toBeDefined();});

복사해서�붙여넣기

it('스핀박스를�생성할�수�있다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then expect(spinbox).toBeDefined();});

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then expect(spinbox).toBeDefined();});

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then const input = spinbox .find('input') .getDOMNode();

expect(input.value).toEqual(200);});

import React from 'react';

const Spinbox = () => ( <div> <input type=“text" /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

import React from 'react';

const Spinbox = () => ( <div> <input type="text" defaultValue="200" /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then const input = spinbox .find('input') .getDOMNode();

const actualValue = Number(input.value); expect(actualValue).toEqual(200);});

import React from 'react';

const Spinbox = () => ( <div> <input type="text" defaultValue="200" /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

넘나�신경�쓰이는�것!

TDD는��테스트와�구현을�반복하면서��계단을�하나씩�쌓아가는�여정�

“테스트�코드”는��어떻게�검증해야�하지?

두�번째�요구사항

기본값은�200�

값�입력�

증가�버튼을�클릭하여�값이�1�증가�

감소�버튼을�클릭하면�값이�1�감소

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then const input = spinbox .find('input') .getDOMNode();

const actualValue = Number(input.value); const defaultValue = 200;

expect(actualValue).toEqual(defaultValue);});

it('입력�폼에�999를�입력할�수�있다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then const input = spinbox .find('input') .getDOMNode();

const actualValue = Number(input.value); const defaultValue = 200;

expect(actualValue).toEqual(defaultValue);});

it('입력�폼에�999를�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />);

// when const input = spinbox.find('input'); input.simulate('change', { target: { value: '999' } });

// then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(999);});

import React from 'react';

const Spinbox = () => ( <div> <input type="text" defaultValue="200" /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

import React from 'react';

const Spinbox = () => ( <div> <input type="text" defaultValue="200" onChange={ () => { this.setState({ value: 999 }) } } /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

아놬!�무상태�컴포넌트!

import React from 'react';

const Spinbox = () => ( <div> <input type="text" defaultValue="200" /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

안전할�때�까지�되돌리기!

it('입력�폼에�숫자�값을�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />);

// when const input = spinbox.find('input'); input.simulate('change', { target: { value: '999' } });

// then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(999);});

it.skip('입력�폼에�숫자�값을�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />);

// when const input = spinbox.find('input'); input.simulate('change', { target: { value: '999' } });

// then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(999);});

import React from 'react';

const Spinbox = () => ( <div> <input type="text" defaultValue="200" /> <button>▲</button> <button>▼</button> </div>);

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

import React from ‘react';

class Spinbox extends React.Component { render() { return ( <div> <input type="text" defaultValue="200" /> <button>▲</button> <button>▼</button> </div> ); }}

Spinbox.defaultProps = {};Spinbox.propTypes = {};

export default Spinbox;

it.skip('입력�폼에�숫자�값을�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />);

// when const input = spinbox.find('input'); input.simulate('change', { target: { value: '999' } });

// then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(999);});

it('입력�폼에�999를�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />);

// when const input = spinbox.find('input'); input.simulate('change', { target: { value: '999' } });

// then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(999);});

class Spinbox extends React.Component { componentWillMount() { this.state = { value: 200 }; } render() { return ( <div> <input type="text" value={ this.state.value } onChange={ () => { this.setState({ value: 999 }) } } /> <button>▲</button> <button>▼</button> </div> ); }}// 생략 …

class Spinbox extends React.Component { componentWillMount() { this.state = { value: 200 }; } render() { return ( <div> <input type="text" value={ this.state.value } onChange={ () => { this.setState({ value: 999 }) } } /> <button>▲</button> <button>▼</button> </div> ); }}// 생략 …

class Spinbox extends React.Component { componentWillMount() { this.state = { value: 200 }; this.handleChangeInput = this.handleChangeInput.bind(this); }

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button>▲</button> <button>▼</button> </div> ); }

handleChangeInput() { this.setState({ value: 999 }); }}

it('스핀박스를�생성할�수�있다.', () => { // given // when const spinbox = mount(<Spinbox />);

// then expect(spinbox).toBeDefined();});

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { … });

it('입력�폼에�999를�입력할�수�있다.', () => { … });

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { … });

it('입력�폼에�999를�입력할�수�있다.', () => { … });

it('입력�폼에�0을�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />);

// when const input = spinbox.find('input'); input.simulate('change', { target: { value: '0' } });

// then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(0);});

handleChangeInput() { this.setState({ value: 999 });}

handleChangeInput(e) { this.setState({ value: e.target.value });}

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { // given // when const spinbox = mount(<Spinbox />); // 생략…});

it('입력�폼에�999를�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />); // 생략…

});

it('입력�폼에�0을�입력할�수�있다.', () => { // given const spinbox = mount(<Spinbox />); // 생략…});

let spinbox;

beforeEach(() => { spinbox = mount(<Spinbox />);});

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { … });

it('입력�폼에�999를�입력할�수�있다.', () => { … });

it('입력�폼에�0을�입력할�수�있다.', () => { … });

it('입력�폼에�999을�입력할�수�있다.', () => { // then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(999);});

it('입력�폼에�0을�입력할�수�있다.', () => { // then const inputNode = input.getDOMNode(); const actualValue = Number(inputNode.value);

expect(actualValue).toEqual(0);});

let spinbox;

beforeEach(() => { spinbox = mount(<Spinbox />);});

function getValueFromInputNode(input) { const inputNode = input.getDOMNode(); return Number(inputNode.value);}

it('입력�폼에�999를�입력할�수�있다.', () => { // …생략 // then const actualValue = getValueFromInputNode(input); expect(actualValue).toEqual(999);});

it('입력�폼에�0을�입력할�수�있다.', () => { // …생략 // then const actualValue = getValueFromInputNode(input); expect(actualValue).toEqual(0);});

세�번째�요구사항

기본값은 200

값 입력

증가 버튼을 클릭하여 값이 1 증가

감소 버튼을 클릭하면 값이 1 감소

it('증가�버튼을�클릭하여�값을�1�증가시킬�수�있다.', () => { // given // when const input = spinbox.find('input'); input.simulate('change', { target: { value: '0' } });

// then const actualValue = getValueFromInputNode(input); expect(actualValue).toEqual(0);});

it('증가�버튼을�클릭하여�값을�1�증가시킬�수�있다.', () => { // given const defaultValue = 200;

// when const incrementBtn = spinbox.find('[data-name="increment"]'); incrementBtn.simulate('click');

// then const actualValue = getValueFromInputNode(spinbox.find('input')); expect(actualValue).toEqual(defaultValue + 1);});

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button data-name="increment">▲</button> <button>▼</button> </div> );}

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button data-name="increment" onClick={ () => { this.setState({ value: 201 }) } }>▲</button> <button>▼</button> </div> );}

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button data-name="increment" onClick={ () => { this.setState({ value: 201 }) } }>▲</button> <button>▼</button> </div> );}

componentWillMount() { this.state = { value: 200 }; this.handleChangeInput = this.handleChangeInput.bind(this); this.handleClickIncrement = this.handleClickIncrement.bind(this);}

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button data-name="increment" onClick={ this.handleClickIncrement }>▲</button> <button>▼</button> </div> );}

handleClickIncrement() { this.setState({ value: 201 });}

it('증가�버튼을�세�번�클릭하여�값을�3�증가시킬�수�있다.', () => { // given const defaultValue = 200;

// when const incrementBtn = spinbox.find('[data-name="increment"]'); incrementBtn.simulate('click'); incrementBtn.simulate('click'); incrementBtn.simulate('click');

// then const actualValue = getValueFromInputNode(spinbox.find('input')); expect(actualValue).toEqual(defaultValue + 3);});

handleClickIncrement() { this.setState({ value: 201 });}

handleClickIncrement() { this.setState({ value: this.state.value + 1 });}

it('스핀박스를�생성하면�기본값은�200이어야�한다.', () => { … }); it('입력�폼에�999를�입력할�수�있다.', () => { … }); it('입력�폼에�0을�입력할�수�있다.', () => { … }); it('증가�버튼을�클릭하여�값을�1�증가시킬�수�있다.', () => { … }); it('증가�버튼을�세�번�클릭하여�값을�3�증가시킬�수�있다.', () => { … });

describe('기본값�>', () => { it('스핀박스를�생성하면�기본값이�200이어야�한다.', () => { … });});

describe('값�입력�>', () => { it('입력�폼에�999를�입력할�수�있다.', () => { … }); it('입력�폼에�0을�입력할�수�있다.', () => { … });});

describe('값�증가�>', () => { it('증가�버튼을�클릭하여�값을�1�증가시킬�수�있다.', () => { … }); it('증가�버튼을�세�번�클릭하여�값을�3�증가시킬�수�있다.', () => { … });});

describe(‘값�입력�>', () => { it('입력�폼에�999를�입력할�수�있다.', () => { // given // when const input = spinbox.find('input'); // … 생략 });

it('입력�폼에�0을�입력할�수�있다.', () => { // given // when const input = spinbox.find('input'); // … 생략 });});

describe(‘값�입력�>', () => { let input;

beforeEach(() => { input = spinbox.find('input'); });

it('입력�폼에�999를�입력할�수�있다.', () => { … }); it('입력�폼에�0을�입력할�수�있다.', () => { … });});

describe(‘기본값�>', () => { it('스핀박스를�생성하면�기본값이�200이어야�한다.', () => { … });});

describe('값�입력�>', () => { let input;

beforeEach(() => { input = spinbox.find('input'); });

it('입력�폼에�999를�입력할�수�있다.', () => { … }); it('입력�폼에�0을�입력할�수�있다.', () => { … });});

describe('값�증가�>', () => { const defaultValue = 200; let incrementBtn;

beforeEach(() => { incrementBtn = spinbox.find('[data-name="increment"]'); });

it('증가�버튼을�클릭하여�값을�1�증가시킬�수�있다.', () => { … }); it('증가�버튼을�세�번�클릭하여�값을�3�증가시킬�수�있다.', () => { … });});

얻는�것과�잃는�것

변경의�포인트는�집중�

코드는�덜�직관적

it('증가 버튼을 세 번 클릭하여 값을 3 증가시킬 수 있다.', () => { // given // when incrementBtn.simulate('click'); incrementBtn.simulate('click'); incrementBtn.simulate('click');

// then const actualValue = getValueFromInputNode(spinbox.find('input')); expect(actualValue).toEqual(defaultValue + 3);});

뭘까?

네�번째�요구사항

기본값은�200�

값�입력�

증가�버튼을�클릭하여�값이�1�증가�

감소�버튼을�클릭하면�값이�1�감소

describe('값�증가�>', () => { const defaultValue = 200; let incrementBtn;

beforeEach(() => { incrementBtn = spinbox.find('[data-name="increment"]'); });

it('증가�버튼을�클릭하여�값을�1�증가시킬�수�있다.', () => { // given // when incrementBtn.simulate('click');

// then const actualValue = getValueFromInputNode(spinbox.find('input')); expect(actualValue).toEqual(defaultValue + 1); });});

describe('값�감소�>', () => { const defaultValue = 200; let decrementBtn;

beforeEach(() => { decrementBtn = spinbox.find('[data-name="decrement"]'); });

it('감소�버튼을�클릭하여�값을�1�감소시킬�수�있다.', () => { // given // when decrementBtn.simulate('click');

// then const actualValue = getValueFromInputNode(spinbox.find('input')); expect(actualValue).toEqual(defaultValue - 1); });});

class Spinbox extends React.Component { componentWillMount() { this.state = { value: 200 }; this.handleChangeInput = this.handleChangeInput.bind(this); this.handleClickIncrement = this.handleClickIncrement.bind(this); this.handleClickDecrement = this.handleClickDecrement.bind(this); }

render() { … }

handleClickDecrement() { this.setState({ value: this.state.value - 1 }); }

handleClickIncrement() { … } handleChangeInput(e) { … }}

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button data-name="increment" onClick={ this.handleClickIncrement }>▲</button> <button>▼</button> </div> );}

render() { return ( <div> <input type="text" value={ this.state.value } onChange={ this.handleChangeInput } /> <button data-name="increment" onClick={ this.handleClickIncrement }>▲</button> <button data-name="decrement" onClick={ this.handleClickDecrement }>▼</button> </div> );}

이걸로�충분할까?

불안하다면�망설이지�말고�

테스트�작성!

it('감소�버튼을�세�번�클릭하여�값을�3�감소시킬�수�있다.', () => { // given // when decrementBtn.simulate('click'); decrementBtn.simulate('click'); decrementBtn.simulate('click');

// then const actualValue = getValueFromInputNode(spinbox.find('input')); expect(actualValue).toEqual(defaultValue - 3);});

끝!

감사합니다!

top related