SlideShare a Scribd company logo
1 of 56
Download to read offline
1 <AboutMe
2 name="穴井宏幸"
3 handleName="@pirosikick"
4 titles={[
5 "ヤフー株式会社 第6・7代黒帯(JavaScript)",
6 "リッチラボ株式会社 エンジニア"
7 ]}
8 home="福岡"
9 />
1 <about-me
2 name="穴井宏幸"
3 handle-name="@pirosikick"
4 :titles="[
5 'ヤフー株式会社 第6・7代黒帯(JavaScript)',
6 'リッチラボ株式会社 エンジニア'
7 ]"
8 home="福岡"
9 >






1 // SomeComponent.spec.js
2 import test from 'ava';
3 import sinon from 'sinon';
4 import React from 'react';
5 import Enzyme, { shallow } from 'enzyme';
6 import Adapter from 'enzyme-adapter-react-16';
7
8 import SomeComponent from './SomeComponent';
9
10 // Reactのバージョンによってアダプタを変える
11 // 本来はavaのrequireで設定したほうがよい
12 Enzyme.configure({
13 adapter: new Adapter()
14 });
15
16 test('SomeComponent', t => {
17 // 描画したコンポーネントをラップしたものを返す
18 const wrapper = shallow(<SomeComponent />);
19
20 // ...テストケースを記述する...
21 });
1 // CSSセレクタで探す
2 t.true(wrapper.find('.some-class').exists());
3 // コンポーネント名で探す
4 t.deepEqual(
5 wrapper.find('OtherComponent').at(0).props(),
6 { no: 1 }
7 );
8 // 子要素
9 t.true(wrapper.childAt(1).is('OtherComponent'));
10
11 // propsの再設定
12 const onClick = sinon.spy();
13 wrapper.setProps({ onClick });
14
15 // イベントの再現
16 wrapper.find('.button').simulate('click');
17 t.true(onClick.calledOnce);
1 // someComponent.spec.js
2 import test from 'ava';
3 import { shallow } from '@vue/test-utils';
4
5 import someComponent from './someComponent.vue';
6 import otherComponent from './otherComponent.vue';
7
8 test('someComponent', t => {
9 // 描画したコンポーネントをラップしたものを返す
10 const wrapper = shallow(someComponent, {
11 propsData: {/* props */}
12 });
13
14 // ...テストケースを記述する...
15 });
1 // CSSセレクタで探す
2 t.true(wrapper.find('.some-class').exists());
3 // コンポーネントで探す
4 t.deepEqual(
5 wrapper.findAll(otherComponent).at(0).props(),
6 { no: 1 }
7 );
8 // 子要素
9 // @vut/test-utilsは子要素の取得はできない?
10
11 // コンポーネントが保持している値の更新
12 wrapper.setProps({/* ... */});
13 wrapper.setData({/* ... */});
14
15 // イベントの再現
16 wrapper.find('.button').trigger('click');
17 // コンポーネントが$emit('clicked')したか
18 t.is(wrapper.emitted().clicked.length, 1);




1 // CustomField.spec.js
2 import ...
3 import CustomField from './CustomField';
4
5 test('render ObjectField', t => {
6 const props = {
7 schema: {
8 type: "array",
9 items: {
10 image: { ... },
11 link: { ... }
12 }
13 },
14 formData: [ ... ]
15 };
16 const wrapper = shallow(<CustomField {...props} />);
17
18 t.true(wrapper.find('ObjectField').exists());
19 });
1 // CustomField.js
2 import ObjectField from 'react-jsonschema-form/...';
3
4 export default function CustomField({
5 schema,
6 formData
7 }) {
8 return <ObjectField />;
9 }
10
1 // アサーションを追加
2 // ObjectFieldに渡すpropsの検証
3 t.deepEqual(
4 wrapper.find('ObjectField').props(),
5 {
6 schema: props.schema.items,
7 formData: props.formData[0]
8 }
9 );
1 // CustomField.js
2 import ObjectField from 'react-jsonschema-form/...';
3
4 export default function CustomField({
5 schema,
6 formData
7 }) {
8 return (
9 <ObjectField
10 schema={schema.items}
11 formData={formData[0]}
12 />
13 );
14 }
1 // CustomField.spec.js
2 // テストケースを追加
3 test(`call onChange with new data
4 if ObjectField is changed`, t => {
5 const props = {
6 schema: { ... },
7 formData: [ ... ],
8 onChange: sinon.spy()
9 };
10 const wrapper = shallow(<CustomField {...props} />);
11
12 // ObjectField is changed
13 wrapper.find('ObjectField').props().onChange({ ... })
14
15 // call onChange with new data
16 t.true(props.onChange.calledOnce);
17 t.deepEqual(props.onChange.args[0], [ ... ]);
18 })
1 // CustomField.js
2 import ObjectField from 'react-jsonschema-form/...';
3
4 export default function CustomField({
5 schema,
6 formData,
7 onChange
8 }) {
9 return (
10 <ObjectField
11 schema={schema.items}
12 formData={formData[0]}
13 onChange={data => {
14 onChange([data, ...formData.slice(1)]);
15 })
16 />
17 );
18 }














1 // 実装
2 <Button data-testid="add-btn">追加</Button>
3 <Button data-testid="remove-btn">削除</Button>
4
5 // テスト
6 // セレクタや並び順では変更に弱い
7 // const rmeoveButton = wrapper.find('Button').at(1);
8 // カスタムデータ属性を使う
9 const removeButton
10 = wrapper.find('[data-testid="remove-btn"]');
11
12 // ヘルパーを作っておくと便利
13 const findByTestId = (wrapper, testid) =>
14 wrapper.find(`[data-testid="${testid}"]`);
1 <!--
2 someComponent.vue
3 - otherComponentはslot(子要素)を受け取れる
4 -->
5 <template>
6 <other-component>
7 <div class="slot-contents"></div>
8 </other-component>
9 </template>
1 // someComponent.spec.js
2 // shallowでは子コンポーネントがスタブされる
3 const wrapper = shallow(someComponent);
4 // slotは描画されないので以下は失敗する
5 t.true(wrapper.find('.slot-contents').exists();
6
7 // mountならうまくいく
8 const wrapper = mount(someComponent);
9 t.true(wrapper.find('.slot-contents').exists();
1 <!-- someComponent.vue -->
2 <template>
3 ...
4 <div class="mounted">
5 {{ isMounted ? 'mounted!' : 'not mounted' }}
6 </div>
7 </template>
8 <script>
9 export default {
10 data () {
11 return { isMounted: false }
12 },
13 mounted () {
14 // shallowでも呼ばれる
15 this.isMounted = true
16 }
17 }
18 </script>
1 // someComponent.spec.js
2 // mountedは呼ばれるがtemplateは更新されていない
3 // 以下は全て通る
4 t.true(wrapper.vm.isMounted);
5 t.is(wrapper.find('.mounted').text(), 'not mounted');
6
7 // updateを実行するとtemplateに反映される
8 wrapper.update();
9 t.is(wrapper.find('.mounted').text(), 'mounted!!');














Inside Frontend 2 #insideFE

More Related Content

What's hot

T sql の parse と generator
T sql の parse と generatorT sql の parse と generator
T sql の parse と generator
Oda Shinsuke
 
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられるアップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
Brian Gesiak
 
Javaセキュアコーディングセミナー東京第3回演習の解説
Javaセキュアコーディングセミナー東京第3回演習の解説Javaセキュアコーディングセミナー東京第3回演習の解説
Javaセキュアコーディングセミナー東京第3回演習の解説
JPCERT Coordination Center
 
Introduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGoodIntroduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGood
Atsuhiro Kubo
 
Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発
Hidekazu Nakamura
 
Javaセキュアコーディングセミナー東京第3回演習
Javaセキュアコーディングセミナー東京第3回演習Javaセキュアコーディングセミナー東京第3回演習
Javaセキュアコーディングセミナー東京第3回演習
JPCERT Coordination Center
 
Qunit再入門 (Version 1.10.0 編)
Qunit再入門 (Version 1.10.0 編)Qunit再入門 (Version 1.10.0 編)
Qunit再入門 (Version 1.10.0 編)
Koji Nakamura
 

What's hot (20)

T sql の parse と generator
T sql の parse と generatorT sql の parse と generator
T sql の parse と generator
 
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられるアップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
 
Javaセキュアコーディングセミナー東京第3回演習の解説
Javaセキュアコーディングセミナー東京第3回演習の解説Javaセキュアコーディングセミナー東京第3回演習の解説
Javaセキュアコーディングセミナー東京第3回演習の解説
 
Development app-with-elixir
Development app-with-elixirDevelopment app-with-elixir
Development app-with-elixir
 
オープンソースでExcelレポートプログラミング
オープンソースでExcelレポートプログラミングオープンソースでExcelレポートプログラミング
オープンソースでExcelレポートプログラミング
 
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
 
TypeScript 言語処理系ことはじめ
TypeScript 言語処理系ことはじめTypeScript 言語処理系ことはじめ
TypeScript 言語処理系ことはじめ
 
Laravel勉強会(データベーステスト編)
Laravel勉強会(データベーステスト編)Laravel勉強会(データベーステスト編)
Laravel勉強会(データベーステスト編)
 
xUnit Test Patterns - Chapter19
xUnit Test Patterns - Chapter19xUnit Test Patterns - Chapter19
xUnit Test Patterns - Chapter19
 
Composable Callbacks & Listeners
Composable Callbacks & ListenersComposable Callbacks & Listeners
Composable Callbacks & Listeners
 
Introduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGoodIntroduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGood
 
from old Java to modern Java
from old Java to modern Javafrom old Java to modern Java
from old Java to modern Java
 
Java8 コーディングベストプラクティス and NetBeansのメモリログから...
Java8 コーディングベストプラクティス and NetBeansのメモリログから...Java8 コーディングベストプラクティス and NetBeansのメモリログから...
Java8 コーディングベストプラクティス and NetBeansのメモリログから...
 
Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発Java scriptによるテスト駆動開発
Java scriptによるテスト駆動開発
 
CPANの依存モジュールをもう少し正しく検出したい
CPANの依存モジュールをもう少し正しく検出したいCPANの依存モジュールをもう少し正しく検出したい
CPANの依存モジュールをもう少し正しく検出したい
 
Javaセキュアコーディングセミナー東京第3回演習
Javaセキュアコーディングセミナー東京第3回演習Javaセキュアコーディングセミナー東京第3回演習
Javaセキュアコーディングセミナー東京第3回演習
 
PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門
 
2017年夏のPerl
2017年夏のPerl2017年夏のPerl
2017年夏のPerl
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
 
Qunit再入門 (Version 1.10.0 編)
Qunit再入門 (Version 1.10.0 編)Qunit再入門 (Version 1.10.0 編)
Qunit再入門 (Version 1.10.0 編)
 

Similar to Inside Frontend 2 #insideFE

Jenkins plugin memo
Jenkins plugin memoJenkins plugin memo
Jenkins plugin memo
Kiyotaka Oku
 
Ibm worklight デモ環境とサンプルコード
Ibm worklight デモ環境とサンプルコードIbm worklight デモ環境とサンプルコード
Ibm worklight デモ環境とサンプルコード
K Kimura
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration
Kazuki Nakajima
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
Yuki Takei
 

Similar to Inside Frontend 2 #insideFE (20)

React Native GUIDE
React Native GUIDEReact Native GUIDE
React Native GUIDE
 
Java EE8 Report
Java EE8 ReportJava EE8 Report
Java EE8 Report
 
test
testtest
test
 
Sencha ug3 siesta_share
Sencha ug3 siesta_shareSencha ug3 siesta_share
Sencha ug3 siesta_share
 
Apache Torqueについて
Apache TorqueについてApache Torqueについて
Apache Torqueについて
 
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
 
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
 
Jenkins plugin memo
Jenkins plugin memoJenkins plugin memo
Jenkins plugin memo
 
Try Jetpack
Try JetpackTry Jetpack
Try Jetpack
 
Angular2 rc.1 unit testing overview
Angular2 rc.1 unit testing overviewAngular2 rc.1 unit testing overview
Angular2 rc.1 unit testing overview
 
イマドキの現場で使えるJavaライブラリ事情
イマドキの現場で使えるJavaライブラリ事情イマドキの現場で使えるJavaライブラリ事情
イマドキの現場で使えるJavaライブラリ事情
 
Android test tutorial
Android test tutorialAndroid test tutorial
Android test tutorial
 
分散ストリーム処理フレームワーク Apache S4
分散ストリーム処理フレームワーク Apache S4分散ストリーム処理フレームワーク Apache S4
分散ストリーム処理フレームワーク Apache S4
 
traceur-compilerで ECMAScript6を体験
traceur-compilerで ECMAScript6を体験traceur-compilerで ECMAScript6を体験
traceur-compilerで ECMAScript6を体験
 
From Swing to JavaFX - SwingからJavaFXへのマイグレーションガイド
From Swing to JavaFX  - SwingからJavaFXへのマイグレーションガイドFrom Swing to JavaFX  - SwingからJavaFXへのマイグレーションガイド
From Swing to JavaFX - SwingからJavaFXへのマイグレーションガイド
 
Ibm worklight デモ環境とサンプルコード
Ibm worklight デモ環境とサンプルコードIbm worklight デモ環境とサンプルコード
Ibm worklight デモ環境とサンプルコード
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
 
ViewModel テスト難しすぎ問題 by saiki iijima in Android Test Night #9
ViewModel テスト難しすぎ問題 by saiki iijima in Android Test Night #9ViewModel テスト難しすぎ問題 by saiki iijima in Android Test Night #9
ViewModel テスト難しすぎ問題 by saiki iijima in Android Test Night #9
 

Recently uploaded

研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
atsushi061452
 

Recently uploaded (14)

5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
 
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイルLoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
 
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
 
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
 
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdfネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
 
Intranet Development v1.0 (TSG LIVE! 12 LT )
Intranet Development v1.0 (TSG LIVE! 12 LT )Intranet Development v1.0 (TSG LIVE! 12 LT )
Intranet Development v1.0 (TSG LIVE! 12 LT )
 
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
 
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
 
Keywordmap overview material/CINC.co.ltd
Keywordmap overview material/CINC.co.ltdKeywordmap overview material/CINC.co.ltd
Keywordmap overview material/CINC.co.ltd
 
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
 
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
 
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアルLoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
 
情報を表現するときのポイント
情報を表現するときのポイント情報を表現するときのポイント
情報を表現するときのポイント
 
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
 

Inside Frontend 2 #insideFE

  • 1.
  • 2. 1 <AboutMe 2 name="穴井宏幸" 3 handleName="@pirosikick" 4 titles={[ 5 "ヤフー株式会社 第6・7代黒帯(JavaScript)", 6 "リッチラボ株式会社 エンジニア" 7 ]} 8 home="福岡" 9 />
  • 3. 1 <about-me 2 name="穴井宏幸" 3 handle-name="@pirosikick" 4 :titles="[ 5 'ヤフー株式会社 第6・7代黒帯(JavaScript)', 6 'リッチラボ株式会社 エンジニア' 7 ]" 8 home="福岡" 9 >
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19. 1 // SomeComponent.spec.js 2 import test from 'ava'; 3 import sinon from 'sinon'; 4 import React from 'react'; 5 import Enzyme, { shallow } from 'enzyme'; 6 import Adapter from 'enzyme-adapter-react-16'; 7 8 import SomeComponent from './SomeComponent'; 9 10 // Reactのバージョンによってアダプタを変える 11 // 本来はavaのrequireで設定したほうがよい 12 Enzyme.configure({ 13 adapter: new Adapter() 14 }); 15 16 test('SomeComponent', t => { 17 // 描画したコンポーネントをラップしたものを返す 18 const wrapper = shallow(<SomeComponent />); 19 20 // ...テストケースを記述する... 21 });
  • 20. 1 // CSSセレクタで探す 2 t.true(wrapper.find('.some-class').exists()); 3 // コンポーネント名で探す 4 t.deepEqual( 5 wrapper.find('OtherComponent').at(0).props(), 6 { no: 1 } 7 ); 8 // 子要素 9 t.true(wrapper.childAt(1).is('OtherComponent')); 10 11 // propsの再設定 12 const onClick = sinon.spy(); 13 wrapper.setProps({ onClick }); 14 15 // イベントの再現 16 wrapper.find('.button').simulate('click'); 17 t.true(onClick.calledOnce);
  • 21. 1 // someComponent.spec.js 2 import test from 'ava'; 3 import { shallow } from '@vue/test-utils'; 4 5 import someComponent from './someComponent.vue'; 6 import otherComponent from './otherComponent.vue'; 7 8 test('someComponent', t => { 9 // 描画したコンポーネントをラップしたものを返す 10 const wrapper = shallow(someComponent, { 11 propsData: {/* props */} 12 }); 13 14 // ...テストケースを記述する... 15 });
  • 22. 1 // CSSセレクタで探す 2 t.true(wrapper.find('.some-class').exists()); 3 // コンポーネントで探す 4 t.deepEqual( 5 wrapper.findAll(otherComponent).at(0).props(), 6 { no: 1 } 7 ); 8 // 子要素 9 // @vut/test-utilsは子要素の取得はできない? 10 11 // コンポーネントが保持している値の更新 12 wrapper.setProps({/* ... */}); 13 wrapper.setData({/* ... */}); 14 15 // イベントの再現 16 wrapper.find('.button').trigger('click'); 17 // コンポーネントが$emit('clicked')したか 18 t.is(wrapper.emitted().clicked.length, 1);
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29. 1 // CustomField.spec.js 2 import ... 3 import CustomField from './CustomField'; 4 5 test('render ObjectField', t => { 6 const props = { 7 schema: { 8 type: "array", 9 items: { 10 image: { ... }, 11 link: { ... } 12 } 13 }, 14 formData: [ ... ] 15 }; 16 const wrapper = shallow(<CustomField {...props} />); 17 18 t.true(wrapper.find('ObjectField').exists()); 19 });
  • 30. 1 // CustomField.js 2 import ObjectField from 'react-jsonschema-form/...'; 3 4 export default function CustomField({ 5 schema, 6 formData 7 }) { 8 return <ObjectField />; 9 } 10
  • 31. 1 // アサーションを追加 2 // ObjectFieldに渡すpropsの検証 3 t.deepEqual( 4 wrapper.find('ObjectField').props(), 5 { 6 schema: props.schema.items, 7 formData: props.formData[0] 8 } 9 );
  • 32. 1 // CustomField.js 2 import ObjectField from 'react-jsonschema-form/...'; 3 4 export default function CustomField({ 5 schema, 6 formData 7 }) { 8 return ( 9 <ObjectField 10 schema={schema.items} 11 formData={formData[0]} 12 /> 13 ); 14 }
  • 33.
  • 34. 1 // CustomField.spec.js 2 // テストケースを追加 3 test(`call onChange with new data 4 if ObjectField is changed`, t => { 5 const props = { 6 schema: { ... }, 7 formData: [ ... ], 8 onChange: sinon.spy() 9 }; 10 const wrapper = shallow(<CustomField {...props} />); 11 12 // ObjectField is changed 13 wrapper.find('ObjectField').props().onChange({ ... }) 14 15 // call onChange with new data 16 t.true(props.onChange.calledOnce); 17 t.deepEqual(props.onChange.args[0], [ ... ]); 18 })
  • 35. 1 // CustomField.js 2 import ObjectField from 'react-jsonschema-form/...'; 3 4 export default function CustomField({ 5 schema, 6 formData, 7 onChange 8 }) { 9 return ( 10 <ObjectField 11 schema={schema.items} 12 formData={formData[0]} 13 onChange={data => { 14 onChange([data, ...formData.slice(1)]); 15 }) 16 /> 17 ); 18 }
  • 37.
  • 38.
  • 39.
  • 41. 1 // 実装 2 <Button data-testid="add-btn">追加</Button> 3 <Button data-testid="remove-btn">削除</Button> 4 5 // テスト 6 // セレクタや並び順では変更に弱い 7 // const rmeoveButton = wrapper.find('Button').at(1); 8 // カスタムデータ属性を使う 9 const removeButton 10 = wrapper.find('[data-testid="remove-btn"]'); 11 12 // ヘルパーを作っておくと便利 13 const findByTestId = (wrapper, testid) => 14 wrapper.find(`[data-testid="${testid}"]`);
  • 42.
  • 43. 1 <!-- 2 someComponent.vue 3 - otherComponentはslot(子要素)を受け取れる 4 --> 5 <template> 6 <other-component> 7 <div class="slot-contents"></div> 8 </other-component> 9 </template> 1 // someComponent.spec.js 2 // shallowでは子コンポーネントがスタブされる 3 const wrapper = shallow(someComponent); 4 // slotは描画されないので以下は失敗する 5 t.true(wrapper.find('.slot-contents').exists(); 6 7 // mountならうまくいく 8 const wrapper = mount(someComponent); 9 t.true(wrapper.find('.slot-contents').exists();
  • 44. 1 <!-- someComponent.vue --> 2 <template> 3 ... 4 <div class="mounted"> 5 {{ isMounted ? 'mounted!' : 'not mounted' }} 6 </div> 7 </template> 8 <script> 9 export default { 10 data () { 11 return { isMounted: false } 12 }, 13 mounted () { 14 // shallowでも呼ばれる 15 this.isMounted = true 16 } 17 } 18 </script>
  • 45. 1 // someComponent.spec.js 2 // mountedは呼ばれるがtemplateは更新されていない 3 // 以下は全て通る 4 t.true(wrapper.vm.isMounted); 5 t.is(wrapper.find('.mounted').text(), 'not mounted'); 6 7 // updateを実行するとtemplateに反映される 8 wrapper.update(); 9 t.is(wrapper.find('.mounted').text(), 'mounted!!');
  • 46.
  • 47.
  • 48.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.