SlideShare une entreprise Scribd logo
1  sur  56
Télécharger pour lire hors ligne
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

Contenu connexe

Tendances

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
 

Tendances (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 編)
 

Similaire à 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
 

Similaire à 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
 

Dernier

Dernier (7)

新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 

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.