Slide 1

Slide 1 text

PHPUnitで 単発/月額決済ありサービスを 自動テストしている話 株式会社NoSchool CTO / meijin

Slide 2

Slide 2 text

目次 - 自己紹介 - Pay.jpを用いた決済の仕組みと月額決済の苦悩 - PHPUnitを用いたPay.jp SDK実行のテストコード記述 - 告知 2

Slide 3

Slide 3 text

自己紹介 3

Slide 4

Slide 4 text

自己紹介 - twitter:   @meijin_garden - career: 奈良高専 → LIFULL → NoSchool CTO - skill: Laravel/Nuxt.ts/Next.js/Nest.JS/AWS(Fargate)/Firebase 4

Slide 5

Slide 5 text

オンライン家庭教師マナリンクを開発しています 5 オンライン家庭教師の CtoCマッチング。 単発課金の指導コースと 月額課金の指導コースがあり、 ご家庭ユーザーがクレカで決済する。

Slide 6

Slide 6 text

Pay.jpを用いた決済の仕組み 6

Slide 7

Slide 7 text

月額決済機能のシーケンス図(※一部割愛) 7

Slide 8

Slide 8 text

月額決済機能のシーケンス図(※一部割愛) 8 ● とにかく実行手順が多い ○ 決済種別の判定→認可判定→決済処理 →支払いステータス更新・先生の売上データ更新 →メール等の通知 ● ローカルでテストするのが面倒 ○ いちいちNuxtを起動してテスト用のクレカを入力するのか? ○ 月額課金の2ヶ月目以降の動作テストが無理

Slide 9

Slide 9 text

テストコードの作成方針 9

Slide 10

Slide 10 text

入出力の結合テストだけでも書く - APIを叩く結合テストだけは絶対に書く - 入力 - 決済エンドポイントにトークン等のPOST - 出力 - DBへの保存、Pay.jpへの通信、メール等の通知実行 - 単体テストは必要に応じて - 結合テストさえあればリファクタや改修に取り組める 10

Slide 11

Slide 11 text

Pay.jp SDKのラッパー実装を やってみた 11

Slide 12

Slide 12 text

Pay.jp SDKを呼び出すだけのラッパーを内製する 12 Charge::createがPay.jp SDKの処理。 これを単に呼び出すだけのクラスを作る。

Slide 13

Slide 13 text

Pay.jpラッパーを作ると嬉しいこと 13 - (前提)Pay.jpのAPIは公式SDK経由で実行する - https://github.com/payjp/payjp-php - ラッパーによってSDKへの依存度が下がる - コードから直接Pay.jp SDKを実行すると依存度が強くなる - SDKの仕様変更の影響範囲がテストコードも含め増えてしまう - ラッパーへのin/outは内製のValueObjectにする

Slide 14

Slide 14 text

ラッパーを利用するときはInjectionする 14 Dependency Injectionすることで、 テスト時にモックに差し替えが可能になる。

Slide 15

Slide 15 text

テストコードでモックに差し替え 15 app->instance()メソッドにより モックをコードに注入できる。

Slide 16

Slide 16 text

所感① - [KEEP]実際の決済処理を実行せずにテストできる - 開発が高速 - Pay.jpのテスト環境を汚さない - リファクタが捗る(リリース後、改修のたびにリファクタをしている) - [PROBLEM]Mock化のコードなどが職人芸になりがち - 職人芸をまとめたオリジナルの基底TestCaseクラスを作ったり READMEを書いたりして職人芸の緩和に務める 16

Slide 17

Slide 17 text

所感② - [TRY]interfaceにして、app->bind()で差し替える - class PayjpCharge implements ChargeInterface - app->bind(ChargeInterface::class, MockChargeInterface::class) - Mockery::mock('alias:(以下略)’)の書き方よりは慣れ親しんでいる &型安全 - 決済以外の処理は基本的にInterfaceに寄せているので合わせていきたい - 必ずErrorをthrowする実装クラスでTransactionのテストもできる 17

Slide 18

Slide 18 text

その他試行錯誤していること 18

Slide 19

Slide 19 text

複数のPay.jpラッパーをまとめるAdaptorの開発 19 一度に複数のラッパーを実行するので、 【定期課金の実行】といった責務で まとめたAdaptor/Interfaceを作り、 色々なサブスク機能で再利用可能にする

Slide 20

Slide 20 text

サブスク更新時のテスト 20

Slide 21

Slide 21 text

POSTされるデータのFixtureを作る - Pay.jpから、課金後1ヶ月毎に 登録したCallback Endpointに課金EventがPOSTされる仕様 - このタイミングで先生の売上更新があるのでめっちゃ重要 - Eventの中にサブスクIDとか金額が含まれている - それで該当のサブスクの検索とか売上更新処理をやる - このEventのFixtureを作る 21

Slide 22

Slide 22 text

例:サブスク更新時のEventを返す関数 22 テスト用のサブスクIDなどを受け取って、 Payjp\Event型の課金Eventを返すファクトリ。 テスト時はEventオブジェクトをモック化して テストデータのIDで更新処理をテストできる!

Slide 23

Slide 23 text

GitHub Actionsの設定 23

Slide 24

Slide 24 text

GitHub Actionsの設定 24 kirschbaumdevelopment/laravel-test-runner:7.4 を使わせてもらっています

Slide 25

Slide 25 text

自動テストを導入した所感 - [KEEP]リファクタリングの障壁が下がる - [KEEP]ドメイン知識の薄い業務委託のエンジニアさんにも 決済処理に関わってもらいやすい≒リスクが小さい - [PROBLEM]GitHub Actionsは実行時間で課金なので、 いずれは実行時間を短縮する工夫が要るかも。 25

Slide 26

Slide 26 text

最後に告知 26

Slide 27

Slide 27 text

Webエンジニア採用中!(副業でお試し勤務OK) - Nuxt.js/Next.js/NestJS/Laravel/AWS(Fargate)/Firebase/Pay.jp - テストコード(PHPUnit/jest)×GitHub Actions×Dockerデプロイ - 現在フルタイム3名(内開発1名...寂しい)、ご家庭・先生方の双方のニーズや課題 をヒアリングしつつ売上を立てていく日々 - ベンチャーなのでもちろんSOも検討します オンライン家庭教師という新しい生き方を広めつつ、 既存の学習市場のニーズを満たしていく事業の話を聞きたい方、お声がけください! 27 名人さん | マナリンクCTO https://twitter.com/Meijin_garden

Slide 28

Slide 28 text

ご清聴ありがとうございました 28