Slide 1

Slide 1 text

テストを⾃動化するのをやめ、 ⾃動テストを作ろう Takuya Suemura @ Autify, Inc.

Slide 2

Slide 2 text

末村 拓也 (すえむら たくや) Twitter: @tsueeemura Autify テスト⾃動化スペシャリスト 開発者 兼 サポート 兼 エバンジェリスト 現場猫 → 底辺PHPer → QAエンジニア → 現職 CodeceptJS コントリビュータ SoftwareDesign誌 6〜8⽉号『はじめよう,⾼速 E2Eテスト』

Slide 3

Slide 3 text

Autifyについて WebアプリのE2Eテスト⾃動化の ためのツール AIによるシナリオの⾃動修復 (セルフヒーリング) リアルデバイス & Dockerコンテ ナでの実⾏に対応 コーディング不要で 誰でもすぐ使える https://autify.com

Slide 4

Slide 4 text

今⽇のテーマ

Slide 5

Slide 5 text

テスト⾃動化

Slide 6

Slide 6 text

テスト⾃動化をやめて、 ⾃動テストを作ろう

Slide 7

Slide 7 text

テスト⾃動化 ≠ ⾃動テスト

Slide 8

Slide 8 text

テスト⾃動化の成果物 ≠ ⾃動テスト

Slide 9

Slide 9 text

⼿動テストを素朴に⾃動化しても、 ⾃動テストにはならない

Slide 10

Slide 10 text

今⽇話すこと なぜテスト⾃動化は失敗しやすいのか ⼿動テストと、その⾃動化、そして⾃動テストはどう違うのか ⾃動テストとはどうあるべきか ⼿動テストの⾃動化に価値はないのか とくに断りがない限りはE2Eテストについての⾔及です (E2Eテスト: システム全てを統合した状態で主にUIからやるテスト)

Slide 11

Slide 11 text

なぜテスト⾃動化は 失敗しやすいのか

Slide 12

Slide 12 text

本題に⼊る前にコンテキストの説明 E2Eテストとは テスト⾃動化とは

Slide 13

Slide 13 text

E2Eテストとは ビルド済みのアプリケーションに対して Webブラウザやモバイル端末から 主にGUIを通してテストを実⾏すること システムテストと呼ばれたりもする QA(品質管理)とかテスターとかが⼿動でポチポチやることが多い ⼿動のものを「⼿動テスト」、⾃動のものを「E2Eテスト」と呼んだりする⼈ もいるが、今回は実⾏⼿段を問わず全てE2Eテストと呼称する

Slide 14

Slide 14 text

よくある開発の流れ ローカルで開発 プルリクエストを出す メインブランチにマージ ステージングサーバにデプロイ E2Eテスト 問題がなければ本番にリリース

Slide 15

Slide 15 text

テスト⾃動化とは 主に⼿動で⾏ってきたE2Eテストを⾃動化すること Selenium, Puppeteer, Cypress, TestCafe, Appiumなどを使う 主に省⼒化や⾼頻度化が⽬的 副次的に実⾏者によるバラツキの削減などもある テスト⼿順は⼿動テストのものを流⽤する 開発の流れそのものは変えない

Slide 16

Slide 16 text

Checking vs Exploring

Slide 17

Slide 17 text

Checking vs Exploring

Slide 18

Slide 18 text

テスト⾃動化が失敗する(機能しなくなる)よくあるパターン ⼿動で頑張ってたテストを⾃動化してみたはいいが……︖ 突然動かなくなる (メンテナンスコストが⾼い) アプリケーションは動いてるがテストは落ちる 不安定で成功したり失敗したりする 思ったより効果が出ない (費⽤対効果が⼩さい) テストしてないところでばかりバグが出る 早期発⾒に繋がらない

Slide 19

Slide 19 text

⼿動テストを⾃動化するとたくさんの観点が失われる # ログインページ ## ログインできる 1. URL /login にアクセスする 2. ユーザー名に takuya と⼊⼒する 3. パスワードに insecure と⼊⼒する 4. ログインボタン をクリックする 5. ホーム画⾯ が表⽰されることを確認

Slide 20

Slide 20 text

describe('ログインページ', () => { it('ログインできる', () => { browser.get('/login') // URL /login にアクセスする $('#username').setValue('takuya') // ユーザー名に takuya と⼊⼒する $('#password').setValue('insecure') // パスワードに insecure と⼊⼒する $('a.btn.btn-primary').click() // ログインボタン をクリックする assert.equal(browser.url, '/home') // ホーム画⾯が表⽰されることを確認 }) }) ロケータ(要素探索のキー)がセマンティックじゃなくなった ユーザー名を⼊れるフィールドっぽいやつ → #username ログインボタンっぽいやつ → a.btn.btn-primary アサーション(検証)が弱くなった ホーム画⾯っぽいページ → /home というURLである

Slide 21

Slide 21 text

assert.ok( $('a[href="https://yahoo.co.jp"]') .isExisting() ) CSSが読み込まれてないけど Aタグが存在するからヨシ︕

Slide 22

Slide 22 text

なぜ⾃動化したテストだけが「腐る」のか︖ 実はユニットテストでも同じことは起きている 違いは開発サイクル ユニットテストは「開発の中で」実⾏・修正される ⾃動化したテストは「開発が終わってから」実⾏・修正される もともと⼿動テストがそういう流れでやってたことの名残 リリース直前にテストが壊れているのが分かった場合、壊れたままリリースする ことがある 結果、メンテナンスが後回しになってしまう これが「腐る」の正体 実⾏頻度が⾼く、開発中も実⾏されるようになると、テストは腐りにくくなる

Slide 23

Slide 23 text

単純に頻度を⾼めればいいのか︖ 頻度を⾼める(例えば1回/⽇→10回/⽇)にしても問題は解決しない 回数ではなく実⾏環境の幅を増やさないといけない 例えば、もともとステージングだけで実⾏していたなら、それを開発環境や CI環境でも実⾏できるようにする ⼿動テストを単純に⾃動化しても、 前提条件 や 事前準備 は⾃動化されない 特定の環境に依存したテストケースになってしまう 環境や特定のテストデータに依存している限り、開発サイクルに寄せることはで きない 依存している前提条件をクリアにし、どの環境でも実⾏できるテストにする

Slide 24

Slide 24 text

(まとめ)テスト⾃動化が失敗してしまう理由 開発サイクルの中でテストが実⾏・メンテナンスされていない テストコードのアップデートが遅れ、製品に問題はないのに「E2Eテストだけ 失敗する」になりがち 実⾏タイミングが遅く、バグの早期発⾒に繋がらない 観点が不明瞭なままテストコードに落とし込んでしまう 「ホーム画⾯が表⽰されることを確認」→「URLが /home であることを確 認」のような不正確な翻訳が⽣まれる ⼈間がテストする場合にくらべ発⾒が少なくなってしまう

Slide 25

Slide 25 text

テスト⾃動化と ⾃動テストの 違いとは︖

Slide 26

Slide 26 text

⾃動テストの⼀般的なイメージ コードで書かれる xUnitとかRSpecとか TDD・リファクタリング 安全な開発のためにやるもの (なんとなく)QAとかテスターがやるやつとはちょっと違う

Slide 27

Slide 27 text

⾃動テストと型検査、Linter ⾃動テスト: アプリケーションが期待した動きをしないとリリースできない 型検査: 型に不整合があるとビルドが通らない Linter: 不適切な書き⽅をすると警告が出る それぞれの対応する領域に対して強制⼒を持つ

Slide 28

Slide 28 text

アプリケーションと⾃動テストの関係 どちらもプロダクトの⼀部 どちらも仕様を具現化したもの アプリケーションはそれ単体では意味をなさない 誰かに使われてはじめて価値を⽣み出す アプリケーションは振る舞いを具現化したもの ⾃動テストは使い⽅を具現化したもの ⾃動テストはアプリケーションを束縛する アプリケーションの特定の挙動に依存することでその挙動を束縛する 互いにコードとして書かれ、実⾏可能な形で記述されていることで強制⼒を 発揮する

Slide 29

Slide 29 text

⾃動テストとは プロダクトの⼀部であり、同時に開発される 開発サイクルの中で実装される (テストフェーズの⾃動化ではない) アプリケーションの挙動に強く依存し、 アプリケーションをより強固にする 静的解析などと同様に頻繁に実⾏され、 仕様からの逸脱を常に防ぐ

Slide 30

Slide 30 text

素朴なテスト⾃動化は部分的な変化のみをもたらす - ⼿動テスト テスト⾃動化 ⾃動テスト 開発サイクル 開発とは別 開発とは別 開発と同時 頻度 少 少 多 実⾏⼿順 あいまい・暗黙的 厳密・具体的 厳密・具体的 検証⽅法 発⾒的 保証的 保証的 太字は⾃動化によって変わってしまうもの ⾚字は⾃動化しても残り続けるもの

Slide 31

Slide 31 text

おれたちが本当に 欲しかったものは︖

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

テスト⾃動化の動機 ⼈間からの脱却 単純作業からの解放 リソース不⾜の解決 属⼈化防⽌ 検証作業のバラツキを減らす ⾼速化・⾼頻度化 リリーススピードの向上 いつでもテスト リファクタリング 上司のご意向

Slide 38

Slide 38 text

(再掲)⼿動テストとテスト⾃動化、そして⾃動テストの違い - ⼿動テスト テスト⾃動化 ⾃動テスト 開発サイクル 開発とは別 開発とは別 開発と同時 頻度 少 少 多 実⾏⼿順 あいまい・暗黙的 厳密・具体的 厳密・具体的 検証⽅法 発⾒的 保証的 保証的 太字は⾃動化によって変わってしまうもの ⾚字は⾃動化しても残り続けるもの

Slide 39

Slide 39 text

なぜテスト⾃動化では ⾼速・⾼頻度が 実現されないのか

Slide 40

Slide 40 text

⾼頻度化に向けて(1) シナリオの最適化 ⼿動テスト向けに最適化された状態 あるテストで作られたデータを別のテストで再利⽤している 「新規作成」のシナリオで作ったデータを「更新」で再利⽤ 特定の環境にしか存在しないテスト⽤データを使っている

Slide 41

Slide 41 text

⾼頻度化に向けて(1) シナリオの最適化 ⾃動テスト向けに最適化する ⽬的︓CIでの実⾏や並列実⾏との相性を良くする 常に新しいデータを作成する GUIではなくAPIやコマンドで作成する 実⾏が他のテストに影響しないようにする 例えば、テストデータの作成時にランダムな(重複しない)⽂字列を使う 環境依存の箇所を排除する 環境変数を使う

Slide 42

Slide 42 text

⼿動テスト最適化 機能1 機能2 機能3 ログイン 機能1 ログイン 機能2 ログイン 機能3 機能1の データ作成 機能1, 2の データ作成 ログイン ログアウト ログイン ログアウト ⾃動テスト最適化 テスト⽤の ユーザー作成 事前準備 実⾏ 事後処理 機能1の データ作成 テストデータの 削除

Slide 43

Slide 43 text

⾃動テストらしいテスト⾃動化 開発中も含めさまざまな環境でテストされている ⾼頻度(理想はコミットorプッシュごと)に実⾏され不具合を防ぐ 安定性が⾼い 他のテストシナリオからの影響を受けない 何度も、同時に実⾏されても問題ない(冪等性がある) テストシナリオが短く保たれている テスト観点以外の操作はAPIやコマンド(サーバサイドスクリプト)で⾏う

Slide 44

Slide 44 text

HAPPY END! (?)

Slide 45

Slide 45 text

⾼頻度は実現できたが、⼈間からは脱却できたか︖ ⼈間からの脱却 ← こっちがまだ 単純作業からの解放 リソース不⾜の解決 属⼈化防⽌ 検証作業のバラツキを減らす ⾼速化・⾼頻度化 リリーススピードの向上 いつでもテスト リファクタリング 上司のご意向

Slide 46

Slide 46 text

参考: テスト⾃動化の8原則 1. ⼿動テストはなくならない 2. ⼿動でおこなって効果のないテストを⾃動化しても無駄である 3. ⾃動テストは書いたことしかテストしない 4. テスト⾃動化の効⽤はコスト削減だけではない 5. ⾃動テストシステムの開発は継続的におこなうものである 6. ⾃動化検討はプロジェクト初期から 7. ⾃動テストで新種のバグが⾒つかることは稀である 8. テスト結果分析という新たなタスクが⽣まれる https://sites.google.com/site/testautomationresearch/test_automation_principle

Slide 47

Slide 47 text

参考: テスト⾃動化の8原則 3. ⾃動テストは書いたことしかテストしない ⼈間による⼿動テストは、テストケースの記述に対して驚くほど広範な要素を暗 黙的にテストしている。これに対し、操作、合否判定を厳密に記述する必要があ る⾃動テストにおいては、おのずと視野は「記述された様に」限定される。ユー ザ名とパスワードを⼊⼒してログインする、といったテストが⾃動化されていた として、その⾃動テストは仮にログイン画⾯に表⽰されているロゴが競合他社の ものであったとしても、それに気づくことはない。 (太字は発表者によるもの) https://sites.google.com/site/testautomationresearch/test_automation_principle

Slide 48

Slide 48 text

アサーション地獄もまた「素朴な⾃動化」の⼀つ

Slide 49

Slide 49 text

再掲 assert.ok( $('a[href="https://yahoo.co.jp"]') .isExisting() ) CSSが読み込まれてないけど Aタグが存在するからヨシ︕

Slide 50

Slide 50 text

参考: テスト⾃動化の8原則 7. ⾃動テストで新種のバグが⾒つかることは稀である 運⽤に乗った⾃動テストは基本的に「枯れた」テストケースを対象とするため、 ほとんどの種類のバグはテストケースを枯らす過程、あるいは⾃動テストを実装 する過程で既に⼈間によって発⾒されているはずである。 多くの運⽤に乗った⾃ 動テストの意義は「⼀度動いたはずの機能がうっかり壊れる」ことを最速で発⾒ することにある。 ただし、⼿順が同じでデータの種類が膨⼤なテストを⾃動化す る場合、ファジング、テストパターンを有機的に⽣成できるAPIレイヤのテスト、 ブラウザやRDBなどのバージョンアップの影響を受けていないことを確認するテ ストなど、いくつかの例外もある。 (太字は発表者によるもの)

Slide 51

Slide 51 text

(再掲)テスト⾃動化が失敗するよくあるパターン ⼿動で頑張ってたテストを⾃動化してみたはいいが……︖ 突然動かなくなる (メンテナンスコストが⾼い) アプリケーションは動いてるがテストは落ちる 不安定で成功したり失敗したりする 思ったより効果が出ない (費⽤対効果が⼩さい) テストしてないところでばかりバグが出る 早期発⾒に繋がらない

Slide 52

Slide 52 text

開発サイクル寄りのかっちりしたテストとは 別のアプローチが必要

Slide 53

Slide 53 text

ふんわり

Slide 54

Slide 54 text

「ふんわり ≒ ⼿動テストよりの」⾃動テスト - ⼿動(ふんわり)テスト テスト⾃動化 ⾃動(かっちり)テスト 開発サイクル 開発とは別 開発とは別 開発と同時 頻度 少 少 多 実⾏⼿順 あいまい・暗黙的 厳密・具体的 厳密・具体的 検証⽅法 発⾒的 保証的 保証的

Slide 55

Slide 55 text

「ふんわり」が必要な理由 「かっちり」では⾒落としてしまうものがある 環境依存のデータに依存するものを検知できない 例)⼤量の関連データを持ったユーザーのみ正常に動作しない アサーションを書いてないものを検知できない 明らかにおかしいけど⼿順書に書かれてないからヨシ︕ 特にレイアウト崩れとかは弱い 全部 getComputedStyle でやっちゃう︖

Slide 56

Slide 56 text

「ふんわり」が必要な理由 「かっちり」はそもそもE2Eらしくない かっちりしたテストで検証するのはクリーンで焦点を絞ったテスト E2Eはそもそもダーティでリアルな状況下でのテストが⽬的のはず ステージングや本番にしかないデータ ブラウザやデバイスなどの実⾏環境 リアルな環境で「正しそうに」動くことを確認するものが欲しい

Slide 57

Slide 57 text

「ふんわり ≒ ⼿動テストより」 の⾃動テストは実現可能か︖

Slide 58

Slide 58 text

ふんわりしたロケータ かっちりテストとは異なり、開発サイクルの中でメンテされるとは限らないので ふんわり≒宣⾔的なロケーティングが必要 // これはよくない $('#username').click() // これは次善の策 $('[data-test=username]').click() // こう書けると良さそう $('ユーザー名').click()

Slide 59

Slide 59 text

ふんわりしたロケータ(1) セマンティックロケータ ⽂⾔や構造による「意味のある」ロケータ #username ではなく ユーザー名 のように表⽰されている⽂⾔で指定 例えばinputならラベルやプレースホルダを使う xxという⽂⾔を持つモーダルの中のyyというボタン のようなUI構造で指定 // CodeceptJSでは⽂⾔や構造による指定をサポートしている I.click('ユーザー名') // 「本当に送信しますか︖」という⽂⾔のあるモーダルの中から within(locate('.modal'.withText('送信先は間違いありませんか︖'), () => { // 「はい」と「送信」をクリック I.click('はい') I.click('送信') })

Slide 60

Slide 60 text

ID: not matched class: matched alter text: matched coodinate: not matched image source: matched .... Confidence: 80% ふんわりしたロケータ(2) AIによる要素探索 (マルチロケータ、 セルフヒーリング) 複数のロケータで複合的に探索 ⾒つかった要素の特徴でシナリ オをアップデート 開発サイクルとテストのサ イクルのズレを吸収

Slide 61

Slide 61 text

ふんわりした検証 アサーション地獄を回避したい 全ての検証項⽬を地道に定義するのはメンテナンス地獄にも繋がる ⼈間がパッと⾒て分かるレベルのエラーを(定義せずに)回避したい 表⽰されるべきものが表⽰されていない レイアウトが崩れている

Slide 62

Slide 62 text

ふんわりした検証 画像⽐較(ビジュアルリグレッション) テスト実⾏ごとのスクリーンショットを⽐較する スタイル崩れなどの検出に有効 ⽐較から除外する箇所の定義が別途必要 広告 検索結果 撮影時の座標がズレると全てが終わる 位置ずれ補正 Awesome Visual Regression Testing: 画像⽐較テストツールのリンク集 https://github.com/mojoaxel/awesome-regression-testing_

Slide 63

Slide 63 text

ふんわりした検証 その他考えうる施策 ページパフォーマンス、エラー監視 デッドリンク検知 ⾃然⾔語解析 テキストの意味が⼤幅に変わっていないか ⽂法の誤り etc...

Slide 64

Slide 64 text

かっちりとふんわり どっちがいいの︖

Slide 65

Slide 65 text

テストを「網のように」考える バグはなるべく上層でキャッチするようにしつつ、下にも網を広げる ユニットテスト 統合テスト APIテスト UIコンポーネントテスト かっちりしたE2Eテスト ふんわりしたE2Eテスト ⼿動での探索的テスト

Slide 66

Slide 66 text

テストタイプによる使い分け例 かっちりテスト向け 機能テスト 回帰テスト ふんわりテスト向け 互換性(クロスブラウザ・マルチデバイス)テスト ユースケーステスト スモークテスト

Slide 67

Slide 67 text

今⽇伝えたかったこと ⼿動テストを素朴に⾃動化しても、⾃動テストにはならない 素朴なテスト⾃動化はどっちつかずのものになってしまう 新規バグは⾒つからず、そのくせメンテ⼯数はガッツリ持っていく ⾃動テストらしい具体性をもったかっちりした⾃動テストと、⼿動テストのよう にファジーなふんわりした⾃動テストというアプローチがある テスト⾃動化をゴールにするのではなく、エントリーポイントとして使い、さら に発展させてほしい

Slide 68

Slide 68 text

Enjoy Testing! spatial.chatの H1 で僕と握⼿︕