Slide 1

Slide 1 text

リーダブルテストコード 〜メンテナンスしやすい  テストコードを作成する  方法を考える〜 1 #DevSumiB

Slide 2

Slide 2 text

はじめに 2

Slide 3

Slide 3 text

はじめに 本セッションで扱うこと ● 読みやすいテストコードについて ● メンテナンスしやすいテストコードについて 本セッションで扱わない(可能性が高い)こと ● 自動テストの書き方 ● テスト駆動開発のやり方

Slide 4

Slide 4 text

登壇者の依頼経緯 ● twadaさん: 翻訳記事「【翻訳】テスト駆動開発の定義」を通じて、 テストリストやTODOリストといった 設計部分のステップの重要性について語っている ● 末村さん: 自著『テスト自動化実践ガイド』の中で 「テストコードに意図を込める」という章を書いている ● 🥦: 翻訳書籍『The BDD Books - Formulation』の中で、 読みやすいテストシナリオについて語っている

Slide 5

Slide 5 text

各登壇者の立ち位置 Dev QA Unit E2E

Slide 6

Slide 6 text

アジェンダ ● はじめに ● twadaさんの発表 ● 末村さんの発表 ● 🥦の発表 ● パネルディスカッション ● おわりに

Slide 7

Slide 7 text

twadaさんの発表 7

Slide 8

Slide 8 text

テストコードの認知負荷 和田卓人 @t_wada

Slide 9

Slide 9 text

自己紹介 和田卓人 (わだ たくと) プログラマ、テスト駆動開発者 学生時代にソフトウェア工学を学び、オブジェクト指向分析/設計に傾倒。執筆活動や 講演、ハンズオンイベントなどを通じてテスト駆動開発を広めようと努力している。 『プログラマが知るべき97のこと』(オライリージャパン、2010)監修。『SQLアン チパターン』(オライリージャパン、2013)監訳。『テスト駆動開発』(オーム社、 2017)翻訳。『事業をエンジニアリングする技術者たち』(ラムダノート、2022)編 者。テストライブラリ power-assert-js 作者。 X(Twitter): @t_wada GitHub: @twada Bluesky: @twada

Slide 10

Slide 10 text

アンチパターン1:情報が少なすぎる

Slide 11

Slide 11 text

アンチパターン2:情報が多すぎる

Slide 12

Slide 12 text

課題外在性負荷を下げる: 名前、構造、情報量に気を配る https://gihyo.jp/dev/serial/01/savanna-letter/0007

Slide 13

Slide 13 text

課題外在性負荷を下げる: 名前、構造、情報量に気を配る テストの本体部分は、 重要でない情報や紛らわしい情報は全く含まずに、 テストを理解するのに必要な情報を全部含むべきである

Slide 14

Slide 14 text

末村さんの発表 14

Slide 15

Slide 15 text

AI powered Quality Engineering Platform 2024.06.20 コンテキストを明⽰して テストコードを⾃⼰説明的にしよう 末村 拓也 2024.9.18 AI powered Quality Engineering Platform

Slide 16

Slide 16 text

末村 拓也 - Quality Evangelist at Autify - 倉庫内軽作業→Web開発者→QAエンジニ ア→テクニカルサポート→マーケター - 品質・テスト周りの議論が好き 自己紹介

Slide 17

Slide 17 text

E2Eテストコードは手続き的になりがち ● いま、どのページにいるのか ● いま、どんなデータがあるはずなのか こうした文脈=コンテキストが暗黙のうちに発生してしまうと 認知負荷が高くなってしまう コンテキスト

Slide 18

Slide 18 text

// 店舗スタッフとしてログインする I.amOnPage("/"); I.click("ログインする"); I.fillField("ユーザー名", "admin"); I.fillField("パスワード", "admin"); I.click("ログイン") // この後の処理は、暗黙に「店舗スタッフとしてログイン後」を期待されている I.click("スタッフ用マイページ") コンテキストが明示されない例 (1) ログイン状態

Slide 19

Slide 19 text

I.amStoreStaff( fn => { I.amOnPage("/"); I.click("ログインする"); I.fillField("ユーザー名", "admin"); I.fillField("パスワード", "admin"); I.click("ログイン") fn() }) ログイン状態に入る関数を作ってしまおう

Slide 20

Slide 20 text

// この中で実行される処理はログイン済み状態であるべき I.amStoreStaff( (I) => { // ... ここに店舗スタッフとして実行したい処理を書く I.click("スタッフ用マイページ") }) ブロックの中はログイン状態なのが明確になる

Slide 21

Slide 21 text

// ここから商品一覧ページ I.amOnPage("/items"); const itemContainer = locate("tr").withText("トマト") I.click("商品を編集", itemContainer); // ここから商品編集ページ I.fillField("商品名", "プチトマト"); I.click("変更"); // ここまで商品編集ページ // ここから商品一覧ページ 同じ要領でページ遷移も

Slide 22

Slide 22 text

I.shouldBeOnItemListPage(I => { const itemContainer = locate('tr').withText("トマト") I.click("商品を編集", itemContainer(itemName)); I.shouldBeOnItemDetailPage(I => { I.fillField("商品名", "プチトマト"); I.click("変更"); }) }) 「あるページにいる」というコンテキストを表現する

Slide 23

Slide 23 text

// テスト用の商品を追加する I.click("商品を追加する"); const itemName = `牛ハラミ弁当-テス ト-${utils.now.format("YYYYMMDDHHmmss")}`; I.fillField("商品名", itemName); I.fillField("商品説明", "テスト用の商品です"); I.fillField("価格", "500"); I.click("追加"); データの準備も

Slide 24

Slide 24 text

// テスト用の商品があるという前提条件が重要であり、手順はどうでもいい const itemName = await I.haveItem() 「〜を持っている」というコンテキストを表現する

Slide 25

Slide 25 text

まとめ ● E2Eテストは手続き的になりがち ● コンテキスト を明示すると コードが自己説明的になり コメントに頼る必要がなくなる

Slide 26

Slide 26 text

🥦の発表 26

Slide 27

Slide 27 text

テストコードには テストの意図を 込めよう(2025年版) ブロッコリー @nihonbuson

Slide 28

Slide 28 text

自己紹介 ● 風間裕也(ブロッコリー) ● 株式会社10X 品質管理チーム ● 副業:B-Testing(個人事業主)として ○ 株式会社MonotaRO (テストコンサルタント) ○ グロース・アーキテクチャ&チームス株式会社 他数社でお手伝い ● 社外活動 ○ JaSST Review(ソフトウェアレビューシンポジウム) 実行委員長 ○ WACATE(テストの合宿型ワークショップ形式勉強会) 実行委員長 ○ SReEE(ソフトウェアレビューをエンジニアリング っぽく捉える会)リーダー SNS上の アイコン

Slide 29

Slide 29 text

翻訳書籍 今日のメイン

Slide 30

Slide 30 text

テストコードを リーダブルにすることで 上がる品質は何か?

Slide 31

Slide 31 text

リーダブルテストコードの効果 リファクタリングはTDDの柱です。 試験性(テスト容易性)はテストファーストが高めますが、 修正性(変更容易性)はリファクタリングが高めます。 保守しやすく変化に強いソフトウェアを支える柱 自動テストとテスト駆動開発 、その全体像 リーダブルなテストコードは修正性(変更容易性 ) を高める

Slide 32

Slide 32 text

理解容易性や説明容易性も重要なのでは? ● 理解容易性(Understandability) ○ 対象の理解しやすい度合い ○ 可読性(Readability)に影響を受ける ● 説明容易性(説明可能性、Explainability) ○ 「今やっていることは何か」を説明できる度合い ○ 読み手のコンテキストにも左右される ○ 特にAI分野では「eXplainable AI(説明可能なAI)」 として注目されている ○ 可読性(Readability)に影響を受ける オリジナル の用語

Slide 33

Slide 33 text

保守性との関係 保守性 テスト容易性 変更容易性 説明容易性 可読性が上がると、説明容易性や理解容易性が上がり、 変更容易性やテスト容易性も上がり、保守性も上がる 理解容易性 可読性

Slide 34

Slide 34 text

会話を通じて 説明容易性を 上げた事例

Slide 35

Slide 35 text

お題と提示しているコードの注意点 ● 10X(私が所属している会社)での 在庫処理に関するテストケース ● 10Xで扱っているプロダクトコードとテストコードに 似たものをJavaの形で書き直したもの ○ 実際のコードにはFlutterで記述している ○ 実際のコードはもっと設計がしっかりしています! ● 分かりやすさ・説明のしやすさ重視のため、 実際のコードよりも処理を単純化して例示している

Slide 36

Slide 36 text

在庫処理のテストケース @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); }

Slide 37

Slide 37 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc(), is(7) ); } これって
 どうして最終的な
 期待値が7なんですか?
 えっとそれは…
 QA 開発者

Slide 38

Slide 38 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } まず、
 商品Aのクラスを
 生成して…
 開発者

Slide 39

Slide 39 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } 元々の在庫数の10個を
 setBaseStockで
 設定して…
 開発者

Slide 40

Slide 40 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } 新たに入庫された6個を
 setReceivedStockで
 設定するので、
 合計の在庫は
 10+6=16個で…
 開発者

Slide 41

Slide 41 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } 得られた在庫数は
 実店舗とネットスーパー上
 での共用となるので、
 全体の在庫の50%を
 ネットスーパー用に
 確保するために、
 setNsCoefficientで
 設定するので、
 16×0.5=8個となって…
 開発者

Slide 42

Slide 42 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } 最後に手動で設定する値
 “setManualOverride”が
 あれば、何があっても
 ネットスーパーの
 在庫数として
 上書きされるので、
 今回の在庫は7個に
 なります。
 開発者

Slide 43

Slide 43 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } なるほどー!
 ありがとうございます。
 QA

Slide 44

Slide 44 text

会話からテストの意図を理解する @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } QA 説明を聞いていると、
 「setManualOverrideがあ れば、
 何があっても 
 ネットスーパーの 
 在庫数として 
 上書きされるので」 
 という部分が
 今回のテストの意図の
 ように聞こえました
 ああ、確かにそうかも
 開発者

Slide 45

Slide 45 text

テストの意図を記載して説明容易性を上げる @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } なので、それを
 そのまま
 テストメソッド名に
 しちゃいましょう!
 QA

Slide 46

Slide 46 text

テストの意図を記載して説明容易性を上げる @Test public void 手動設定の値があれば必ず 最終的な在庫数として上書き設定する { ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } なので、それを
 そのまま
 テストメソッド名に
 しちゃいましょう!
 QA

Slide 47

Slide 47 text

余談:各職種の得意分野 得意分野となる注目点が異なる ● PO…今回のFeatureで実現したいことに注目 ● 開発者…今回のFeatureはどのようにすれば     実現できるかに注目 ● QA…今回のFeatureが「完成した」と   判断するためには   何を確認すれば良いのか(テストの意図)に注目 ※あくまでも得意分野であり、  責任分担している訳ではない

Slide 48

Slide 48 text

テストの意図を テストメソッド名に 書くことの利点

Slide 49

Slide 49 text

利点1.特別な設定値がどれなのか分かる @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); } テストの意図が 書かれていないと、 テストメソッド内 にある、 “10”, “6”, “0.5”, “7” がそれぞれ 特別な意味を 持っている ように見えてしまう

Slide 50

Slide 50 text

利点1.特別な設定値がどれなのか分かる テストの意図が 書かれていると、 重要な箇所が分かる その他の値は、 テストするために 設定した 任意の値だと分かる @Test public void 手動設定の値があれば必ず 最終的な在庫数として 上書き設定 する{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); }

Slide 51

Slide 51 text

利点2.仕様変更時に対応しやすい 例えば、 「手動設定 setManualOverride は考えなくても 良いことになった」 という仕様変更が 出てきたとき… @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); }

Slide 52

Slide 52 text

利点2.仕様変更時に対応しやすい プロダクトコードで setManualOverride() の処理を 削除したとする 連動して、 このテストコードは 通らなくなる @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); }

Slide 53

Slide 53 text

利点2.仕様変更時に対応しやすい テストの意図が 書かれていないと、 このテストが どんなことを したかったのか 改めて読み込む 必要がある @Test public void 手動設定ありのパターン{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); }

Slide 54

Slide 54 text

利点2.仕様変更時に対応しやすい テストの意図が 書かれていることで、 今回はテストケース そのものを削除する 対応で良いと 即時に判断できる @Test public void 手動設定の値があれば必ず 最終的な在庫数として 上書き設定 する{ ProductStock productStock = new ProductStock(“商品A”); productStock.setBaseStock(10); productStock.setReceivedStock(6); productStock.setNsCoefficient(0.5); productStock.setManualOverride(7); assertThat( productStock.calc() , is(7)); }

Slide 55

Slide 55 text

まとめ

Slide 56

Slide 56 text

まとめ ● 何を確認したいテストなのか考えることが大切 ● 会話を通じてテストの意図を見つけ出すことが可能 ● テストの意図を明記しておくことで、 ○ 理解容易性や説明容易性が上がる ○ テストコードの修正性(変更容易性)が上がる

Slide 57

Slide 57 text

パネル ディスカッション 57

Slide 58

Slide 58 text

議論1:それぞれの発表を聞いた感想 ● 各発表の簡単なまとめ ○ twadaさん…適度な情報量と名前や構造に気を配る ○ 末村さん…コンテキストを明示する ○ 🥦…テストの意図をテストメソッド名に示す ● 共感した部分 ● 少し考え方が違った部分 ● 深掘りしたい部分

Slide 59

Slide 59 text

議論2:まず意識する部分は何か? ● テストコードを含むコードのレビュアーを想定 ● 最初にどんな思考をするのか? ● 最初に確認する内容はどこか? ○ 指摘事項 ○ レビュー作業の内容

Slide 60

Slide 60 text

議論3:セルフチェックできる要素は何か ● テストコードを含むコードの作成者を想定 ● リーダブルなテストコードを目指すために、 自分自身でチェックできる項目は何か? ● 最初にAIにチェックを任せるとして、 どんな点に注目してチェックをお願いするか?

Slide 61

Slide 61 text

議論:会場からの質問 ● Slidoを見て答える

Slide 62

Slide 62 text

おわりに 62

Slide 63

Slide 63 text

本セッションの続きはJaSST Tokyoで! ● 同じ登壇者で3/28に登壇します。 (イベント全体は3/27,28の2日間) ● 会場はTODAホール&カンファレンス東京 ○ JR東京駅八重洲中央口から徒歩7分 ○ 銀座線京橋駅から徒歩3分 ● 参加費 ○ 会場参加:11,000円 ○ オンライン参加:8,800円

Slide 64

Slide 64 text

末村さんのお知らせ ブースで販売しています 声かけてくれたらサインします 目印

Slide 65

Slide 65 text

🥦のお知らせ ● 明日もB会場(ココ)で登壇します ○ 明日2/14の12:40~13:20 ○ 開発スピードは上がっている…品質はどうする? ~スピードと品質を両立させるための プロダクト開発の進め方とは~ ● CodeZine Academy ○ 次回は3/14 ○ オフライン開催

Slide 66

Slide 66 text

おしまい 66 ご清聴ありがとうございました!