Reactでユニットテスト #scripty05

652 Views

March 30, 16

スライド概要

2016/3/17の勉強会にて発表された資料です。

SCRIPTY#5 ~フロントエンド紳士・淑女のための勉強会~
http://scripty.connpass.com/event/27752/

profile-image

2023年10月からSpeaker Deckに移行しました。最新情報はこちらをご覧ください。 https://speakerdeck.com/lycorptech_jp

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

Reactでユニットテスト 2016/3/17 ヤフー株式会社 マーケティングソリューションカンパニー開発本部 UIフロントエンド開発部 森本 恭平 (@basara669)

2.

About Me ・元々JSちょこっと ・今はJSしか書いてない ・ぶっちゃけテストとか素人 Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

3.

優しい目で見守ってください。 Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

4.

クライアントサイドの 自動ユニットテストやってますか? Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

5.

ユニットテストの難しいところ ・環境構築が難しい ・ドキュメントが難しい(ってか、英語) ・なんか具体的な例が見つけづらい ・ユニットテストって言葉が良くわからない Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

6.

ってか、 全体的によくわからない!! Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

7.

今日は10分で雰囲気掴めれば 良いかなと思います! Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

8.

ユニットテストってそもそも何を やるものなの? Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

9.

誤解を恐れずに言うなら... Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

10.

if文と一緒!! Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

11.

ユニットテストの手順 1.レンダリングされているであろう状態を作成 2.期待する内容を作成 3.1と2を比較する Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

12.

ほらif文に感じません?w Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

13.

ですが... Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

14.

ユニットテストで難しいところ 1.レンダリングされた状態を作成すること 2.期待する内容を作成(テストケース) Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

15.

Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

16.

ユニットテストで難しいところ 1.レンダリングされた状態を作成すること 2.期待する内容を作成 Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

17.

こういう環境で話を進めます ・アプリケーション自身:React ・テストフレームワーク:mocha ・アサーション:expect ・実行:node ・その他:react-test-utiles Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

18.

こういう環境で話を進めます ・アプリケーション自身:React ・テストフレームワーク:mocha ・アサーション:expect ・実行:node ・その他:react-test-utiles Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

19.

フレームワークとアサーション ・テスティングフレームワーク:テストを記述する関数群を提供し、それらの関数を使って書かれたテストの結果を判定、集計した上で結果を表示する機能 ・アサーションライブラリ:テスティングフレームワークは、テスト結果を管理するライブラリですが、実行結果を判定する ※引用元:http://codezine.jp/article/detail/7367 Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

20.

最初のテスト Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

21.

最初のテスト import expect from 'expect'; describe('first test', () => { });

22.

最初のテスト import expect from 'expect'; describe('first test', () => { it('同じもの比較してるんだからtrue', () => { }); });

23.
[beta]
最初のテスト
import expect from 'expect';
describe('first test', () => {
it('同じもの比較してるんだからtrue', () => {
expect(true).toEqual(true);
});
});
24.

ほらif文っぽいww Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

25.

関数のテスト Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

26.
[beta]
テストする関数
export default function(uniqueNumber,
description) {
const shortKebabDescription = description
.toLowerCase()
.split(' ')
.slice(0, 2)
.join('-');
return `${uniqueNumber}-${shortKebabDescription}`;
}
27.

関数の内容 ・番号と文字列を受け取って ・文字列を小文字に変えて ・文字列をスペースで区切って ・文字列を最初の2つにして、 ・文字列をハイフンでくっつけて、 ・番号とハイフン繋ぎで返却する Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

28.

関数のテスト import expect from 'expect'; describe('createId関数の試験', () => { });

29.
[beta]
関数のテスト
import expect from 'expect';
import createId from './createId';
describe('createId関数の試験', () => {
});
30.
[beta]
関数のテスト
import expect from 'expect';
import createId from './createId';
describe('createId関数の試験', () => {
it('ユニークなIDを返却', () => {
});
});
31.
[beta]
関数のテスト
import expect from 'expect';
import createId from './createId';
describe('createId関数の試験', () => {
it('ユニークなIDを返却', () => {
const actual = createId(123, 'Cool example
description');
});
});
32.
[beta]
関数のテスト
import expect from 'expect';
import createId from './createId';
describe('createId関数の試験', () => {
it('ユニークなIDを返却', () => {
const actual = createId(123, 'Cool example
description');
const expected = `123-cool-example`;
});
});
33.
[beta]
関数のテスト
import expect from 'expect';
import createId from './createId';
describe('createId関数の試験', () => {
it('ユニークなIDを返却', () => {
const actual = createId(123, 'Cool example
description');
const expected = `123-cool-example`;
expect(actual).toEqual(expected);
});
});
34.

DOMのテスト (class編) Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

35.

Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

36.

DOMのテストの難しい所 ・これまではDOMの試験をしようとするとHTMLをJS に変換などが必要だった ・Reactなら既にJSになっている!! Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

37.

Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

38.
[beta]
テストするコンポーネント
import React from 'react';
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
39.
[beta]
import React from 'react';
import expect from 'expect';
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('classのチェック', () => {
expect(actual).toEqual(expected);
});
});
40.
[beta]
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import expect from 'expect';
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('classのチェック', () => {
const renderer = TestUtils.createRenderer();
renderer.render(<CoolComponent greeting='hello world' /
>);
const actual = renderer.getRenderOutput();
});
});
41.
[beta]
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import expect from 'expect';
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('classのチェック', () => {
const renderer = TestUtils.createRenderer();
renderer.render(<CoolComponent greeting='hello world' /
>);
const actual = renderer.getRenderOutput();
console.log(actual)
});
});
42.

CoolComponent { '$$typeof': Symbol(react.element), type: 'div', key: null, ref: null, props: { className: 'red', children: [ [Object], [Object] ] }, _owner: { _currentElement: { '$$typeof': Symbol(react.element), type: [Function: CoolComponent], key: null, ref: null, props: [Object], _owner: null, _store: {} }, _rootNodeID: '.4hqupilt6o', _instance: StatelessComponent { props: [Object], context: {}, refs: {}, updater: [Object], _reactInternalInstance: [Circular], state: null }, _pendingElement: null, _pendingStateQueue: null, _pendingReplaceState: false, _pendingForceUpdate: false, _renderedComponent: { _renderedOutput: [Circular], _currentElement: [Circular] }, _context: {}, _mountOrder: 2, _topLevelWrapper: null, _pendingCallbacks: null }, _store: {} }

43.

CoolComponent { '$$typeof': Symbol(react.element), type: 'div', key: null, ref: null, props: { className: 'red', children: [ [Object], [Object] ] }, _owner: { _currentElement: { '$$typeof': Symbol(react.element), type: [Function: CoolComponent], key: null, ref: null, props: [Object], _owner: null, _store: {} }, _rootNodeID: '.4hqupilt6o', _instance: StatelessComponent { props: [Object], context: {}, refs: {}, updater: [Object], _reactInternalInstance: [Circular], state: null }, _pendingElement: null, _pendingStateQueue: null, _pendingReplaceState: false, _pendingForceUpdate: false, _renderedComponent: { _renderedOutput: [Circular], _currentElement: [Circular] }, _context: {}, _mountOrder: 2, _topLevelWrapper: null, _pendingCallbacks: null }, _store: {} }

44.

DOMのテスト (html編) Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

45.
[beta]
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import expect from 'expect';
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('hello worldが出ている', () => {
const renderer = TestUtils.createRenderer();
renderer.render(<CoolComponent greeting='hello world' />);
const actual = renderer.getRenderOutput();
const expected = <div>hello world!</div>;
expect(actual).toEqual(expected);
});
});
46.
[beta]
23 passing (21ms)
1 failing
1) CoolComponent should render the greeting:
Error: Expected { $$typeof: Symbol(react.element), _owner: { _context: {}, _currentElement: {
{}, key: null, props: { greeting: 'hello world' }, ref: null, type: [Function: CoolComponent] }, _i
nternal: [Function], enqueueForceUpdate: [Function], enqueueReplaceProps: [Function], enqueueReplac
on], enqueueSetProps: [Function], enqueueSetPropsInternal: [Function], enqueueSetState: [Function],
lbacks: null, _pendingElement: null, _pendingForceUpdate: false, _pendingReplaceState: false, _pend
nt: [Circular], _renderedOutput: [Circular] }, _rootNodeID: '.1n30ss7jjeo', _topLevelWrapper: null
f: Symbol(react.element), _owner: { _context: [Object], _currentElement: [Object], _instance: [Obje
lement: null, _pendingForceUpdate: false, _pendingStateQueue: null, _pendingStateQueue: null, _r
_topLevelWrapper: null }, _store: {}, key: null, props: { children: 'Greeting' }, ref: null, type:
ontext: [Object], _currentElement: [Object], _instance: [Object], _mountOrder: 31, _pendingCallback
se, _pendingReplaceState: false, _pendingStateQueue: null, _renderedComponent: [Object], _rootNodeI
key: null, props: { children: [Object] }, ref: null, type: 'div' }, className: 'red', ref: nul
nt), _owner: null, _store: {}, key: null, props: { children: 'hello world!' }, ref: null, type: 'di
+ expected - actual
47.

CoolComponent { '$$typeof': Symbol(react.element), type: 'div', key: null, ref: null, props: { className: 'red', children: [ [Object], [Object] ] }, _owner: { _currentElement: { '$$typeof': Symbol(react.element), type: [Function: CoolComponent], key: null, ref: null, props: [Object], _owner: null, _store: {} }, _rootNodeID: '.4hqupilt6o', _instance: StatelessComponent { props: [Object], context: {}, refs: {}, updater: [Object], _reactInternalInstance: [Circular], state: null }, _pendingElement: null, _pendingStateQueue: null, _pendingReplaceState: false, _pendingForceUpdate: false, _renderedComponent: { _renderedOutput: [Circular], _currentElement: [Circular] }, _context: {}, _mountOrder: 2, _topLevelWrapper: null, _pendingCallbacks: null }, _store: {} }

48.

expect-jsx

49.
[beta]
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import expect from 'expect';
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('hello worldが出ている', () => {
const renderer = TestUtils.createRenderer();
renderer.render(<CoolComponent greeting='hello world' />);
const actual = renderer.getRenderOutput();
const expected = <div>hello world!</div>;
expect(actual).toEqual(expected);
});
});
50.
[beta]
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import expect from 'expect';
import expectJSX from 'expect-jsx';
expect.extend(expectJSX);
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('hello worldが出ている', () => {
const renderer = TestUtils.createRenderer();
renderer.render(<CoolComponent greeting='hello world' />);
const actual = renderer.getRenderOutput();
const expected = <div>hello world!</div>;
});
});
51.
[beta]
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import expect from 'expect';
import expectJSX from 'expect-jsx';
expect.extend(expectJSX);
const CoolComponent = ({greeting}) => (
<div className="red">
<h1>Greeting</h1>
<div>{greeting}!</div>
</div>
);
describe('CoolComponentのテスト', () => {
it('hello worldが出ている', () => {
const renderer = TestUtils.createRenderer();
renderer.render(<CoolComponent greeting='hello world' />);
const actual = renderer.getRenderOutput();
const expected = <div>hello world!</div>;
// expect(actual).toEqualJSX(expected);
expect(actual).toIncludeJSX(expected);
});
});
52.

まとめ ・ユニットテストという言葉に怯えず、値と値を比べる てるだけと考えれば大丈夫! ・DOM回りのテストはReactがやっぱりやりやすい ・役に立ちそうなプラグインはどんどん使う! Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止

53.

ご静聴ありがとうございました。 Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止