Upgrade to Pro — share decks privately, control downloads, hide ads and more …

13年続くレガシーサービスを安全にリリースし続けるためのテスト戦略 / rakus-meetup-osaka-vol8-2020-08-05

kyoshimoto
August 05, 2020

13年続くレガシーサービスを安全にリリースし続けるためのテスト戦略 / rakus-meetup-osaka-vol8-2020-08-05

Rakus Meetup Osaka #8 の発表資料です。
https://rakus.connpass.com/event/161744/

kyoshimoto

August 05, 2020
Tweet

More Decks by kyoshimoto

Other Decks in Technology

Transcript

  1. 自己紹介 • 所属・氏名 • 株式会社ラクス 第四開発部 • 吉元 和仁(よしもとかずひと) •

    仕事 • 2018年10月より自社サービス「配配メール」の開発担当
  2. テストの7原則 1. テストは欠陥があることは示せるが、欠陥がないことは示せない 2. 全数テストは不可能 3. 早期テストで時間とコストを節約 4. 欠陥の偏在 5.

    殺虫剤のパラドックスにご用心 6. テストは状況次第 7. 「バグゼロ」の落とし穴 (参考: http://jstqb.jp/dl/JSTQB-SyllabusFoundation_Version2018V31.J02.pdf)
  3. カバレッジログ出力プログラムを準備 • <?php • xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); • function output_coverage_report_file()

    { • if (!xdebug_code_coverage_started()) { • return; • } • xdebug_stop_code_coverage(false); • $jsonData = json_encode(xdebug_get_code_coverage()); • // コードカバレッジ結果をJSONファイルに出力する • $reportFile = __DIR__ . '/reports/json/coverage-' . microtime(true) . '.json'; • file_put_contents($reportFile, $jsonData); • } register_shutdown_function('output_coverage_report_file'); •
  4. カバレッジログ出力プログラムを準備 • <?php • xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); • function output_coverage_report_file()

    { • if (!xdebug_code_coverage_started()) { • return; • } • xdebug_stop_code_coverage(false); • $jsonData = json_encode(xdebug_get_code_coverage()); • // コードカバレッジ結果をJSONファイルに出力する • $reportFile = __DIR__ . '/reports/json/coverage-' . microtime(true) . '.json'; • file_put_contents($reportFile, $jsonData); • } register_shutdown_function('output_coverage_report_file'); • ② プログラム終了時に実行する コールバック関数を定義
  5. カバレッジログ出力プログラムを準備 • <?php • xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); • function output_coverage_report_file()

    { • if (!xdebug_code_coverage_started()) { • return; • } • xdebug_stop_code_coverage(false); • $jsonData = json_encode(xdebug_get_code_coverage()); • // コードカバレッジ結果をJSONファイルに出力する • $reportFile = __DIR__ . '/reports/json/coverage-' . microtime(true) . '.json'; • file_put_contents($reportFile, $jsonData); • } register_shutdown_function('output_coverage_report_file'); • ③ カバレッジ取得停止
  6. カバレッジ結果出力プログラムを準備 • <?php • xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); • function output_coverage_report_file()

    { • if (!xdebug_code_coverage_started()) { • return; • } • xdebug_stop_code_coverage(false); • $jsonData = json_encode(xdebug_get_code_coverage()); • // コードカバレッジ結果をJSONファイルに出力する • $reportFile = __DIR__ . '/reports/json/coverage-' . microtime(true) . '.json'; • file_put_contents($reportFile, $jsonData); • } register_shutdown_function('output_coverage_report_file'); • ④ カバレッジデータを ファイル出力
  7. カバレッジレポート出力プログラムを準備 • <?php • ini_set('max_execution_time', 0); • ini_set('memory_limit', -1); require_once

    __DIR__ . "/vendor/autoload.php"; • $summaryCoverage = new SebastianBergmann¥CodeCoverage¥CodeCoverage(); • // ... 中略 ... • $summaryCoverage->filter() • ->addDirectoryToWhitelist('/usr/local/myapp', ['.php', '.inc’]); • $reportFiles = glob(__DIR__ . '/reports/json/*.json’); ① レポート用モジュールを生成し、ログデータ読み込み準備
  8. • $countFiles = count($reportFiles); • for ($i = 0; $i

    < $countFiles; $i++) { • printf("[%d/%d] append coverage data %s¥n", • ($i + 1), $countFiles, $reportFiles[$i] • ); • $tmpData = json_decode( • file_get_contents($reportFiles[$i]), JSON_OBJECT_AS_ARRAY); • $summaryCoverage->append($tmpData, 'myapp'); • } カバレッジレポート出力プログラムを準備 ② ログデータを読み込む
  9. • echo "Output html report start..." . PHP_EOL; • $report

    = new SebastianBergmann¥CodeCoverage¥Report¥Html¥Facade(); • $report->process($summaryCoverage, __DIR__ . '/reports/html'); • echo "Output html report completed" . PHP_EOL; カバレッジレポート出力プログラムを準備 ③ カバレッジ結果をHTML出力
  10. PHP設定ファイルの編集 • 「auto_prepend_file」ディレクティブ設定 ; Automatically add files before PHP document.

    ; http://php.net/auto-prepend-file auto_prepend_file = {カバレッジログ出力プログラムのパス}
  11. 利用するライブラリ • Puppeteer • E2Eテスト用ライブラリ • プログラミング言語: JavaScript (https://github.com/puppeteer/puppeteer) •

    Visual Studio Code (IDE環境) • テストプログラム作成のためのIDE環境として利用 (https://azure.microsoft.com/ja-jp/products/visual-studio-code/)
  12. PageObject パターンでユーザ操作をメソッド化 module.exports = class HMPage { //...中略... async input件名(aSubject)

    { await this.page.type('#subject', aSubject); } async click新規作成() { await this.page.waitForXPath('//a[contains(text(), "新規作成")]') const link = await this.page.$x('//a[contains(text(), "新規作成")]') await link[0].click() await this.navigationPromise; } async click次へ() { await this.page.waitForXPath('//a[contains(text(), "次へ")]') const link = await this.page.$x('//a[contains(text(), "次へ")]') await link[0].click() await this.navigationPromise; }
  13. 重要機能テストをテストコード化 • (async () => { • const page =

    new HMPage() //.. 中略... await page.inputログインID("テスト太郎") • await page.inputパスワード("password") • await page.clickBtnログイン() • await page.click新規作成() • await page.click次へ() • actualValue = await page.getTitle() • assert.equal(actualValue, 'メール作成画面', '「メール作成画面」画面が表示される'); • await page.input件名("テストメール件名") • await page.input本文("テストメール本文") • await page.click次へ() • actualValue = await page.getTitle() • assert.equal(actualValue, 'メール確認画面', '「メール確認画面」画面が表示される');
  14. テスト駆動開発への期待 • 納期のプレッシャーを低減 • 開発終盤でも安心して、修正・再設計できる。 • 既存バグや複雑な作業に翻弄されずに、進捗させやすくなる。 • 開発タスクを分業しやすくし、クリティカルパスを低減できる。 •

    スイッチングコストの低減 • 実装結果がすぐにフィードバックされるので、実装に専念できる。 • 調査・実装・デバッグ・既存バグによる作業中断によるスイッチングコ ストを低減できる。 • 開発チームの設計力底上げ • テスト容易性を考慮した設計が行えるようになり、開発チームの設計力 を底上げできる。
  15. テスト駆動開発用ツール • PHPUnit • PHPで動くxUnit系フレームワーク (公式: https://phpunit.de/) • Guzzle •

    PHPで利用できるHTTPクライアント (http://docs.guzzlephp.org/en/stable/index.html)