Submit Search
Upload
ゼロから始めたE2Eテスト
•
2 likes
•
3,578 views
ushiboy
Follow
Niigata.js #2 発表資料
Read less
Read more
Engineering
Report
Share
Report
Share
1 of 44
Download now
Download to read offline
Recommended
MQTTとAMQPと.NET
MQTTとAMQPと.NET
terurou
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
モノビット エンジン
基礎から学ぶ組み込みAndroid
基礎から学ぶ組み込みAndroid
demuyan
シリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのか
Atsushi Nakada
H.264で相互接続 - WebRTC Meetup Tokyo #10
H.264で相互接続 - WebRTC Meetup Tokyo #10
goforbroke
脱RESTful API設計の提案
脱RESTful API設計の提案
樽八 仲川
アジャイル×テスト開発を考える
アジャイル×テスト開発を考える
yasuohosotani
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
Yoshinori Matsunobu
Recommended
MQTTとAMQPと.NET
MQTTとAMQPと.NET
terurou
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
モノビット エンジン
基礎から学ぶ組み込みAndroid
基礎から学ぶ組み込みAndroid
demuyan
シリコンバレーの「何が」凄いのか
シリコンバレーの「何が」凄いのか
Atsushi Nakada
H.264で相互接続 - WebRTC Meetup Tokyo #10
H.264で相互接続 - WebRTC Meetup Tokyo #10
goforbroke
脱RESTful API設計の提案
脱RESTful API設計の提案
樽八 仲川
アジャイル×テスト開発を考える
アジャイル×テスト開発を考える
yasuohosotani
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
Yoshinori Matsunobu
Selenium WebDriver + python で E2Eテスト自動化
Selenium WebDriver + python で E2Eテスト自動化
JustSystems Corporation
Gaming on aws 〜ゲームにおけるAWS最新活用術〜
Gaming on aws 〜ゲームにおけるAWS最新活用術〜
Amazon Web Services Japan
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
増田 亨
Redmineプラグイン導入・開発入門
Redmineプラグイン導入・開発入門
Minoru Maeda
Epic Online Services でできること
Epic Online Services でできること
エピック・ゲームズ・ジャパン Epic Games Japan
今さら聞けない人のためのCI/CD超入門
今さら聞けない人のためのCI/CD超入門
VirtualTech Japan Inc./Begi.net Inc.
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
Takahito Tejima
View Customize Pluginで出来ること
View Customize Pluginで出来ること
onozaty
インフラエンジニアの綺麗で優しい手順書の書き方
インフラエンジニアの綺麗で優しい手順書の書き方
Shohei Koyama
Google Cloud Game Servers 徹底入門 | 第 10 回 Google Cloud INSIDE Games & Apps Online
Google Cloud Game Servers 徹底入門 | 第 10 回 Google Cloud INSIDE Games & Apps Online
Google Cloud Platform - Japan
大規模ゲーム開発における build 高速化と安定化
大規模ゲーム開発における build 高速化と安定化
DeNA
アーキテクチャのレビューについて - JaSST Review '18
アーキテクチャのレビューについて - JaSST Review '18
Yusuke Suzuki
他社製品と比較した際のAuth0のいいところ
他社製品と比較した際のAuth0のいいところ
Satoshi Takayanagi
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
Hiroshi Tokumaru
Redmineのバージョンアップに追従していくための一工夫
Redmineのバージョンアップに追従していくための一工夫
Go Maeda
OSSを活用したIaCの実現
OSSを活用したIaCの実現
Trainocate Japan, Ltd.
Serverless時代のJavaについて
Serverless時代のJavaについて
Amazon Web Services Japan
CircleCIのinfrastructureを支えるTerraformのCI/CDパイプラインの改善
CircleCIのinfrastructureを支えるTerraformのCI/CDパイプラインの改善
Ito Takayuki
Kubernetes環境に対する性能試験(Kubernetes Novice Tokyo #2 発表資料)
Kubernetes環境に対する性能試験(Kubernetes Novice Tokyo #2 発表資料)
NTT DATA Technology & Innovation
5分でわかる Sensor SDK
5分でわかる Sensor SDK
UnityTechnologiesJapan002
Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
Yohei Sato
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs
Takayoshi Tanaka
More Related Content
What's hot
Selenium WebDriver + python で E2Eテスト自動化
Selenium WebDriver + python で E2Eテスト自動化
JustSystems Corporation
Gaming on aws 〜ゲームにおけるAWS最新活用術〜
Gaming on aws 〜ゲームにおけるAWS最新活用術〜
Amazon Web Services Japan
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
増田 亨
Redmineプラグイン導入・開発入門
Redmineプラグイン導入・開発入門
Minoru Maeda
Epic Online Services でできること
Epic Online Services でできること
エピック・ゲームズ・ジャパン Epic Games Japan
今さら聞けない人のためのCI/CD超入門
今さら聞けない人のためのCI/CD超入門
VirtualTech Japan Inc./Begi.net Inc.
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
Takahito Tejima
View Customize Pluginで出来ること
View Customize Pluginで出来ること
onozaty
インフラエンジニアの綺麗で優しい手順書の書き方
インフラエンジニアの綺麗で優しい手順書の書き方
Shohei Koyama
Google Cloud Game Servers 徹底入門 | 第 10 回 Google Cloud INSIDE Games & Apps Online
Google Cloud Game Servers 徹底入門 | 第 10 回 Google Cloud INSIDE Games & Apps Online
Google Cloud Platform - Japan
大規模ゲーム開発における build 高速化と安定化
大規模ゲーム開発における build 高速化と安定化
DeNA
アーキテクチャのレビューについて - JaSST Review '18
アーキテクチャのレビューについて - JaSST Review '18
Yusuke Suzuki
他社製品と比較した際のAuth0のいいところ
他社製品と比較した際のAuth0のいいところ
Satoshi Takayanagi
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
Hiroshi Tokumaru
Redmineのバージョンアップに追従していくための一工夫
Redmineのバージョンアップに追従していくための一工夫
Go Maeda
OSSを活用したIaCの実現
OSSを活用したIaCの実現
Trainocate Japan, Ltd.
Serverless時代のJavaについて
Serverless時代のJavaについて
Amazon Web Services Japan
CircleCIのinfrastructureを支えるTerraformのCI/CDパイプラインの改善
CircleCIのinfrastructureを支えるTerraformのCI/CDパイプラインの改善
Ito Takayuki
Kubernetes環境に対する性能試験(Kubernetes Novice Tokyo #2 発表資料)
Kubernetes環境に対する性能試験(Kubernetes Novice Tokyo #2 発表資料)
NTT DATA Technology & Innovation
5分でわかる Sensor SDK
5分でわかる Sensor SDK
UnityTechnologiesJapan002
What's hot
(20)
Selenium WebDriver + python で E2Eテスト自動化
Selenium WebDriver + python で E2Eテスト自動化
Gaming on aws 〜ゲームにおけるAWS最新活用術〜
Gaming on aws 〜ゲームにおけるAWS最新活用術〜
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
Redmineプラグイン導入・開発入門
Redmineプラグイン導入・開発入門
Epic Online Services でできること
Epic Online Services でできること
今さら聞けない人のためのCI/CD超入門
今さら聞けない人のためのCI/CD超入門
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
View Customize Pluginで出来ること
View Customize Pluginで出来ること
インフラエンジニアの綺麗で優しい手順書の書き方
インフラエンジニアの綺麗で優しい手順書の書き方
Google Cloud Game Servers 徹底入門 | 第 10 回 Google Cloud INSIDE Games & Apps Online
Google Cloud Game Servers 徹底入門 | 第 10 回 Google Cloud INSIDE Games & Apps Online
大規模ゲーム開発における build 高速化と安定化
大規模ゲーム開発における build 高速化と安定化
アーキテクチャのレビューについて - JaSST Review '18
アーキテクチャのレビューについて - JaSST Review '18
他社製品と比較した際のAuth0のいいところ
他社製品と比較した際のAuth0のいいところ
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
Redmineのバージョンアップに追従していくための一工夫
Redmineのバージョンアップに追従していくための一工夫
OSSを活用したIaCの実現
OSSを活用したIaCの実現
Serverless時代のJavaについて
Serverless時代のJavaについて
CircleCIのinfrastructureを支えるTerraformのCI/CDパイプラインの改善
CircleCIのinfrastructureを支えるTerraformのCI/CDパイプラインの改善
Kubernetes環境に対する性能試験(Kubernetes Novice Tokyo #2 発表資料)
Kubernetes環境に対する性能試験(Kubernetes Novice Tokyo #2 発表資料)
5分でわかる Sensor SDK
5分でわかる Sensor SDK
Similar to ゼロから始めたE2Eテスト
Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
Yohei Sato
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs
Takayoshi Tanaka
React Native GUIDE
React Native GUIDE
dcubeio
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門
spring_raining
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
normalian
Spring と TDD
Spring と TDD
Takeshi Ogawa
Ll xcode
Ll xcode
Net Kanayan
TDD勉強会キックオフ for Java
TDD勉強会キックオフ for Java
Yuta Kawadai
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
Shumpei Shiraishi
Monadic Programmingのススメ - Functional Reactive Programmingへのアプローチ
Monadic Programmingのススメ - Functional Reactive Programmingへのアプローチ
Tomoharu ASAMI
はじめてのAngular その1
はじめてのAngular その1
純一 榮枝
Parse.comと始めるBackbone.js入門(jscafe7)
Parse.comと始めるBackbone.js入門(jscafe7)
Ryuma Tsukano
究極のバッチフレームワーク(予定)
究極のバッチフレームワーク(予定)
fumoto kazuhiro
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
JustSystems Corporation
Unit testで定時帰宅!
Unit testで定時帰宅!
Funato Takashi
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
信之 岩永
Swift 2.0 大域関数の行方から #swift2symposium
Swift 2.0 大域関数の行方から #swift2symposium
Tomohiro Kumagai
130710 02
130710 02
openrtm
Archive: Android アプリ開発入門(2015/6/19 社内勉強会)
Archive: Android アプリ開発入門(2015/6/19 社内勉強会)
Yoko TAMADA
第4回勉強会 単体テストのすすめ
第4回勉強会 単体テストのすすめ
hakoika-itwg
Similar to ゼロから始めたE2Eテスト
(20)
Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs
React Native GUIDE
React Native GUIDE
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
Spring と TDD
Spring と TDD
Ll xcode
Ll xcode
TDD勉強会キックオフ for Java
TDD勉強会キックオフ for Java
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
Monadic Programmingのススメ - Functional Reactive Programmingへのアプローチ
Monadic Programmingのススメ - Functional Reactive Programmingへのアプローチ
はじめてのAngular その1
はじめてのAngular その1
Parse.comと始めるBackbone.js入門(jscafe7)
Parse.comと始めるBackbone.js入門(jscafe7)
究極のバッチフレームワーク(予定)
究極のバッチフレームワーク(予定)
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜
Unit testで定時帰宅!
Unit testで定時帰宅!
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
Swift 2.0 大域関数の行方から #swift2symposium
Swift 2.0 大域関数の行方から #swift2symposium
130710 02
130710 02
Archive: Android アプリ開発入門(2015/6/19 社内勉強会)
Archive: Android アプリ開発入門(2015/6/19 社内勉強会)
第4回勉強会 単体テストのすすめ
第4回勉強会 単体テストのすすめ
ゼロから始めたE2Eテスト
1.
ゼロから始めたE2Eテスト Niigata.js #2 ushiboy
2.
自己紹介 ● 新潟市内でWebプログラマ ● 主にWebフロントエンド担当 ○
とある有償フレームワーク ○ React + Redux ● やんごとなき理由でデバイス周りも ○ Raspberry PI + Python ○ ESP32 + C++
3.
今日はE2Eテストの話をします
4.
E2Eテストとは ● End to
End テストの略 ● システム全体が正しく動作することを確認する ○ Webアプリケーションの場合、ユーザーが行うようにブラウザを操作して結果が期待通りか確認 ● 主な特徴 ○ コスト高め ○ 実行時間長め ○ 不安定になりやすい
5.
E2Eテストは基本的に辛い
6.
それでもやらなければならない時がある
7.
自分がE2Eテストを始めたきっかけ ● 独りWebフロントエンドチームでSPAなWebアプリケーションを複数開発 ○ そこそこの規模の複数アプリ間を機能追加・修正のために行ったり来たり ●
機能追加・修正で気づかずにデグレを入れがちだった ○ 単体テストは書いていたがカバーしきれなかった ○ 使っていたフレームワークの都合上、型システムが使えなかった ● 修正を入れた後にざっと全体確認できるようにしたかった
8.
使ってみたテストツール ● Jasmine +
PhantomJS でオレオレ方式 ● Karma ● Selenium WebDriver
9.
最終的にSelenium WebDriver に落ち着いた
10.
ここからは サンプルアプリケーションを使って 雰囲気を見ていきます
11.
サンプルアプリケーション ● ページロードで動的にJSONデータを取得し て一覧表示する ● チェックした行を選択アイテムとする ●
「けってい」クリックで選択アイテムを表示 https://ushiboy.github.io/niigata.js-v2/index.html
12.
(余談)アプリのソースはjQueryベタ書き養殖もの $(function() { var fish
= []; var selectedItems = []; $.ajax('fish.json').done(function(response) { fish = response.fish; var $fishesList = $('#fish-list'); $.each(fish, function(i, r) { $fishesList.append( $('<tr />') .append($('<td />').append($('<input type="checkbox" class="select-row" />').data(r))) .append($('<td />').text(r.name)) ); }); }); }); 省略 https://github.com/ushiboy/niigata.js-v2/blob/master/docs/app.js
13.
テストのための環境を準備 ● npmでSelenium WebDriverをインストール ○
$ npm i -D selenium-webdriver ● ブラウザのWebDriverの設置 ○ https://www.seleniumhq.org/download/ から取得 ● サーバ環境の用意 ○ 自分の場合はDockerで構築 ● テストコードの用意 ○ 自分の場合はmocha + power-assert
14.
WebDriver ● 配布サイト(https://www.seleniumhq.org/download/)から取得 ○ ChromeやIE、Edgeなどは各ブラウザ公式が配布している ●
OSにインストールされているブラウザのバージョンと合わせる必要がある ○ 昨今のブラウザは自動アップデートされるので動かなくなったらバージョンを疑う ○ プロジェクトのリポジトリ配下に置くなら ignore対象にしたほうが良い
15.
ファイル・ディレクトリ構成 . ├── babel.config.js ├── docker-compose.yml ├──
node_modules ├── package-lock.json ├── package.json └── test ├── driver │ └── chromedriver ├── spec │ └── FishList-test.js └── testSetup.js WebDriver置き場 Docker用 テストケース置き場
16.
サーバ環境 ● 自分の場合はDockerで用意 ● 動作に必要なものをまとめて扱うためにdocker-composeを利用 ○
nginx ○ DB ○ Redis ○ アプリケーションサーバ ○ etc..
17.
今回のdocker-compose.yml version: "3" services: web: container_name: "web" image:
nginx:1.17-alpine ports: - "8080:80" volumes: - ../docs:/usr/share/nginx/html
18.
サーバの起動と終了 $ docker-compose up
-d Creating network "case1_default" with the default driver Creating web ... done $ docker-compose down Stopping web ... done Removing web ... done Removing network case1_default
19.
テストコード ● SeleniumのAPIを使ってページ要素を取得・処理していく ○ https://selenium.dev/selenium/docs/api/javascript/ ■
セレクタを使ってDOM要素を取得 ■ DOM要素の操作(キー入力・クリックなど) ■ Wait処理 ○ 非同期APIを多用 ● テストライブラリは非同期なテストに対応しているものを使う ○ 自分の場合mocha ● WebDriver置き場へのパスを通しておく ○ 自分の場合ブートストラップ用の testSetup.jsで
20.
testSetup.js require('@babel/register')(); require('@babel/polyfill'); const path =
require('path'); const driversDirPath = path.join(__dirname, 'driver'); process.env.PATH = process.env.PATH + ':' + driversDirPath;
21.
テストコードの基本構成 const assert =
require('power-assert'); const chrome = require('selenium-webdriver/chrome'); const {Builder, By, Key, until} = require('selenium-webdriver'); describe('FishList', function() { this.timeout(20000); let driver; beforeEach(() => { driver = new Builder() .forBrowser('chrome') .setChromeOptions(new chrome.Options().headless()) .build(); }); afterEach(() => { return driver.quit(); }); it('テストケース', async () => { // テスト内容 }); });
22.
テストケースのコード例 it('一覧が動的に読み込まれること ', async
() => { await driver.get('http://localhost:8080'); let rows; await driver.wait(async () => { rows = await driver.findElement(By.id('fish-list')).findElements(By.tagName('tr')); return rows.length !== 0; }, 5000); assert(rows.length === 3); const cols1 = await rows[0].findElements(By.tagName('td')); assert(await cols1[1].getText() === 'まぐろ'); const cols2 = await rows[1].findElements(By.tagName('td')); assert(await cols2[1].getText() === 'はまち'); const cols3 = await rows[2].findElements(By.tagName('td')); assert(await cols3[1].getText() === 'かつお'); });
23.
Single Page Applicationなどでのコツ ●
動的に描画を扱うページの場合 ○ ページロード後にすべての描画が完了していない ○ 対象の描画完了を待つ必要がある ■ 要素の変化 ■ タイマー ● おすすめは要素の変化を利用する ○ タイマーだと十分な余裕を持った間隔がテスト実行の長さに繋がる ○ 一覧であれば行数の変化とか ○ ローディングマスクが消えたタイミングとか
24.
今回のは行数の変化を監視して待つ例 it('一覧が動的に読み込まれること ', async
() => { await driver.get('http://localhost:8080'); let rows; await driver.wait(async () => { rows = await driver.findElement(By.id('fish-list')).findElements(By.tagName('tr')); return rows.length !== 0; }, 5000); assert(rows.length === 3); const cols1 = await rows[0].findElements(By.tagName('td')); assert(await cols1[1].getText() === 'まぐろ'); const cols2 = await rows[1].findElements(By.tagName('td')); assert(await cols2[1].getText() === 'はまち'); const cols3 = await rows[2].findElements(By.tagName('td')); assert(await cols3[1].getText() === 'かつお'); });
25.
(余談)async/await使わないとこうなる it('一覧が動的に読み込まれること', () =>
{ let rows; return driver.get('http://localhost:8080').then(() => { return driver.wait(() => { return driver.findElement(By.id('fish-list')).findElements(By.tagName('tr')).then(result => { rows = result; return rows.length !== 0; }); }, 5000); }).then(() => { assert(rows.length === 3); }).then(() => { return rows[0].findElements(By.tagName('td')).then(cols => { return cols[1].getText().then(t => { assert(t === 'まぐろ'); });});}).then(() => { return rows[1].findElements(By.tagName('td')).then(cols => { return cols[1].getText().then(t => { assert(t === 'はまち'); });});}).then(() => { return rows[2].findElements(By.tagName('td')).then(cols => { return cols[1].getText().then(t => { assert(t === 'かつお'); });});});});
26.
実行結果 $ npm test >
case1@0.1.0 test /home/ushiboy/Workspace/niigata.js-v2/case1 > mocha --require test/testSetup.js --recursive './test/spec/*.js' FishList ✓ 一覧が動的に読み込まれること (573ms) 1 passing (650ms)
27.
このままテストケースを書いていくと...
28.
発生する問題 ● 画面の仕様変更・修正 ○ HTMLの構造に変更があった場合、該当するすべてのテストコードを修正する必要がある ●
可読性が悪い ○ テストコードを見るのも直すのもキツくなる ○ そしてテストを動かさなくなる
29.
なぜ問題が発生するのか?
30.
注目ポイント it('一覧が動的に読み込まれること ', async
() => { await driver.get('http://localhost:8080'); let rows; await driver.wait(async () => { rows = await driver.findElement(By.id('fish-list')).findElements(By.tagName('tr')); return rows.length !== 0; }, 5000); assert(rows.length === 3); const cols1 = await rows[0].findElements(By.tagName('td')); assert(await cols1[1].getText() === 'まぐろ'); const cols2 = await rows[1].findElements(By.tagName('td')); assert(await cols2[1].getText() === 'はまち'); const cols3 = await rows[2].findElements(By.tagName('td')); assert(await cols3[1].getText() === 'かつお'); });
31.
ページの要素の取得・操作と テストが一緒になっているから
32.
ベタ書き養殖ものと同じことになっていた $(function() { var fish
= []; var selectedItems = []; $.ajax('fish.json').done(function(response) { fish = response.fish; var $fishesList = $('#fish-list'); $.each(fish, function(i, r) { $fishesList.append( $('<tr />') .append($('<td />').append($('<input type="checkbox" class="select-row" />').data(r))) .append($('<td />').text(r.name)) ); }); }); }); 省略
33.
アプリだけでなくテストでも分離する
34.
Page Object Pattern ●
UI(ページ)をページオブジェクトとして抽象化 ○ UI操作をメソッドとして扱う ● テストコードとUI操作を分離する ○ UIに変更があった場合に、テスト自体は変更せずにページオブジェクトの変更に留める ● https://www.seleniumhq.org/docs/06_test_design_considerations.jsp#page-ob ject-design-pattern
35.
今回のアプリケーションでは FishList FishListRow ● 全体を扱うFishList ● 行を扱うFishListRow
36.
FishList const {By, Key,
until} = require('selenium-webdriver'); export class FishList { constructor(driver) { this.driver = driver; } async open() { await this.driver.get('http://localhost:8080'); return this; } async waitForRowToFinishLoading() { await this.driver.wait(async () => { const rows = await this.getRows(); return rows.length !== 0; }, 5000); return this; } async clickSelect() { await this.driver.findElement(By.id('select-button')).click(); return this.driver.switchTo().alert(); } async clickAllCheck() { const check = await this.driver.findElement(By.id('all-check')); check.click(); return this; } async getRows() { const rows = await this.driver.findElement(By.id('fish-list')) .findElements(By.tagName('tr')); return rows.map(r => { return new FishListRow(this.driver, r); }); } }
37.
FishListRow export class FishListRow
{ constructor(driver, el) { this._driver = driver; this._el = el; } async getName() { const cols = await this._el.findElements(By.tagName('td')); return cols[1].getText(); } async clickCheckBox() { await this._el.findElement(By.className('select-row')).click(); return this; } }
38.
Page Objectを利用したテストコード it('一覧が動的に読み込まれること ',
async () => { const p = new FishList(driver); await p.open(); await p.waitForRowToFinishLoading(); const rows = await p.getRows(); assert(rows.length === 3); assert(await rows[0].getName() === 'まぐろ'); assert(await rows[1].getName() === 'はまち'); assert(await rows[2].getName() === 'かつお'); });
39.
素朴 vs Page
Object Pattern 比較 it('一覧が動的に読み込まれること ', async () => { await driver.get('http://localhost:8080'); let rows; await driver.wait(async () => { rows = await driver.findElement(By.id('fish-list')) .findElements(By.tagName('tr')); return rows.length !== 0; }, 5000); assert(rows.length === 3); const cols1 = await rows[0].findElements(By.tagName('td')); assert(await cols1[1].getText() === 'まぐろ'); const cols2 = await rows[1].findElements(By.tagName('td')); assert(await cols2[1].getText() === 'はまち'); const cols3 = await rows[2].findElements(By.tagName('td')); assert(await cols3[1].getText() === 'かつお'); }); it('一覧が動的に読み込まれること ', async () => { const p = new FishList(driver); await p.open(); await p.waitForRowToFinishLoading(); const rows = await p.getRows(); assert(rows.length === 3); assert(await rows[0].getName() === 'まぐろ'); assert(await rows[1].getName() === 'はまち'); assert(await rows[2].getName() === 'かつお'); });
40.
Page Object Patternを採用した結果 ●
UI変更の修正がしやすくなる ● 可読性上がる ● E2Eテストを書くモチベーションが上がる(個人差あり)
41.
そんな感じでテストを書いて...
42.
実行結果(複数) $ npm test >
case1@0.1.0 test /home/ushiboy/Workspace/niigata.js-v2/case1 > mocha --require test/testSetup.js --recursive './test/spec/*.js' FishList ✓ 一覧が動的に読み込まれること (1161ms) ✓ 未選択状態で"けってい"するとアラートがでること (535ms) ✓ 一覧のアイテムを1件選択して"けってい"すると選択結果が表示されること (604ms) ✓ 一覧のアイテムを複数件選択して "けってい"すると選択結果が表示されること (684ms) ✓ 全体チェックをするとすべてのアイテムが選択されること (731ms) ✓ 全部チェックを外すとすべてのアイテムが選択解除になること (845ms) 6 passing (5s)
43.
まとめ ● E2Eテストは辛い ● 工夫して辛さを抑える ○
Page Object Pattern ● 最後は気合い
44.
付録 ● python(pytest)を使ったサンプル ○ https://github.com/ushiboy/niigata.js-v2/tree/master/case2 ○
複数のDocker環境を立ち上げてワーカーで並列にテスト実行する
Download now