Djangoプロジェクトで設定しておくべきこと (6) E2Eテスト
前回の記事の続きです。
今回はDjangoプロジェクトのE2Eテストの仕組み作りについて説明していきます。
E2Eテスト
DjangoのE2Eテストは2通りが考えられます。
- フロントとバックエンドをDjangoフルスクラッチで作成したアプリの動作確認
- バックエンドAPIだけDjangoで作成したAPIの動作確認
どちらも手間がかかる作業ですが、上記の
フロントとバックエンドをDjangoフルスクラッチで作成したアプリ
の方がテストの実装は楽です。
バックエンドとフロントが分離している場合は、
- API側のE2Eテスト
- フロントとバックエンドサーバーを結合したE2Eテスト
の両方を作成、実行する必要があって、フルスクラッチと比較して手間がかかります。
(もちろんフロントとバックエンドサーバーを結合したE2Eテストだけあれば問題ない規模であれば、無駄にテストを増やす必要はありません)
現在はマイクロサービスアーキテクチャが主流なので、APIのE2Eテストとアプリ動作のE2Eテスト両方を書く場合がほとんどでしょう。
また、外部APIを公開しているアプリでは、APIのE2Eテストは避けて通れません。
なので、今回は両方のE2Eテストに対応できるよう
- playwright
- pact
- tavern
のE2Eテストツールについて説明します。
E2Eテストは、導入することによって逆にコストが上がってしまうこともあるので、自分のプロジェクトで本当に必要なのかをよく考えて導入しましょう。
1-1. playwright
playwrightはWebアプリケーションのエンド・ツー・エンドのテストを実装できるツールです。
pythonの他にもNode.js, Java, .Netが利用できます。
使い方はSeleniumに似ていて、htmlのXpathを設定し、クリックやリンク後に表示されるhtmlデータの検証をします。
私の場合、pythonのplaywrightを使っていて、vue3で作成しているフロントのE2Eテストに利用しています。
インストールをすればすぐに使えるので、導入でつまづく箇所はないと思います。
具体的なコードは以下になります。
from playwright.sync_api import Page
HOGE_TAB = '//*[@id="app"]/section/div/div/div[2]/ul/li[2]/a'
def test_F003_10_xxxxxx_hoge_tab(page: Page):
"""
ホゲタブをクリック
"""
page.goto("https://app-front:8000/hoge/new")
page.click(HOGE_TAB)
title = page.text_content('//*[@id="app"]/section/div/div/form/div/div/div[1]/div[1]/label')
assert title == "ほげ"
これを実行するには、フロントサーバーを立ち上げて
pytest
とターミナルで実行します。
実行結果はpytestと同じフォーマットで表示されるので、python使いにはとても扱いやすいです。
一方で、playwrightはメンテナンスを続けることが大変なので仕組み化が重要になってきます。
具体的には以下のような仕組みが効果的です。
- e2eテスト用のDBとテストデータを用意する
- テストコードを適切なレベルでフォルダ分けする
- 共通で利用するXpathは定数化して、散らばらないようにする
- 画面にば番号(コード)をふる
- gitHubActionで強制実行。失敗したらdeployできない
- 全て自動でやろうとしない。手動にも頼る
- テストデータは定期的に変更する時間をとる
- 定期的にリファクタリング
これらの中でも大切なことは
- gitHubActionで強制実行。失敗したらdeployできない
- 全て自動でやろうとしない。手動にも頼る
です。
また、テストコードの管理を厳しくし過ぎてしまうと、エンジニアの士気が下がって生産性が落ちます。
ときには
書いたテストコードを捨てる
という決断も重要です。
playwrightはDjango以外のWEBアプリでも利用できる汎用性の高いE2Eフレームワークです。
しかし、その汎用性の高さ故に費用対効果に気をつけて使う必要があります。
目的と手段を履き違えないように気をつけてください。
1-2. Pact
PactはマイクロサービスのAPI連携の確認をするツールです。
E2Eテストはこうあるべき
という理想を実現したツールです。
E2Eテストを実行していくと必ず発生する事案で、テストに時間がかかりすぎ問題があります。
マイクロサービスはプロジェクトが大きくなってくると、いくつものAPIサーバー(コンテナ)が乱立した状態になる傾向があります。
E2Eテストを実行するには
- 関連する全てのサービスを立ち上げ
- 関連する全てのDBにダミーデータを投入
後に、全てのテストが実行されるのを待つ必要があります。
こうなると、たとえ変更が軽微で、オブジェクト{}を配列[]にしたい程度の変更であっても、長い待ち時間が発生してしまいます。
これでは、開発者の生産性が低下してしまいます。
これを解決するのがPactです。
PactはAPIの呼び出し側をConsumer、APIが呼び出される側をProvider、両者がやりとりするAPIを介するデータをContractと定義します。
このContractをConsumerとProviderが壊していないかをチェックします。
そして、ConsumerとProviderがPactデータをやりとりする仲介サーバー使うことで、開発者全員がCIを通して変更を素早く検知できます。
これにより、変更対象のAPIサーバーがE2Eテスト環境に上がる前に変更を検知することができます。
つまり、
E2Eテストの二つ前の工程である実装の段階で変更を検知できる
ということです。
これは、他のE2Eテストツールと異なる大きな利点です。
手戻りは工程が前であればあるほど、作業時間を節約できます。
しかし、これだけ大きな利点がありながら、私はPactの運用は早々に諦めました。
理由は
- 運用コストと学習コストの大きさ
- 対応している言語が限定されている
です。
Pactはサーバーを用意したり、Pactの仕様を理解して各種プログラミングで実装できる必要があります。人の入れ替わりが激しいIT業界で、このコストの大きさは致命的です。
言語はruby, python, java, golangなどの人気言語に対応していますが、rustなどは用意されていません。
また、rubyのメンテナンスが従実している一方で、他の言語は貧弱なのも気になりました。
なので、PactでE2Eテストを運用するなら
- サーバーがruby(rails)中心
- お金も時間もかかってもいいから新しい技術でメンバースキルの底上げを狙いたい
ようなプロジェクトに適用すると良いと思います。
1-3. Tavern
TavernはAPIの自動テストのためのpytestプラグインです。
現在あるE2Eテストのツールでは、
このTavernがもっともオススメ
です。
私はAPIのE2Eテストでは、このTavernを利用しています。
理由は
- シンプル
- 導入が楽
- テストをyamlで管理できる
からです。
requestの変数化、responseデータの一時記録、auth header対応、テストの使い回し、ファイル分割と
シンプルな作りながら、9割以上のテストはこのTavernだけで完結できます。
例として以下のようなAPIフローのテストしたいと仮定します。
- token取得
- 権限取得
- データ取得
このような場合でもyamlだけで処理を完結させることが可能です。
responseデータの記録は
token: token
とyamlに記載するだけでtoken変数を次のAPIで使いまわすことができます。
欠点は
- シンプルすぎてテストできないケースがある
ことです。
しかし、9割以上のテストはカバーできるはずです。
カバーできないテストは、手動やpytestなどで補完すれば問題ありません。
個人的に激推しなので、是非利用してみてください。
まとめ
今回はE2Eテストの設定について説明しました。
今回紹介したツールの他にもE2Eテストのツールはたくさんあって、Autify(有料), Karate, Cypress, Cucumber, Seleniumなど様々なツールをこれまでに使ってきました。
しかし残念なことに
「これだ!」
という完璧なツールにはまだ出会ったことがありません。
どれも一長一短があって、時間が経つとメンテナンスされなくなるというケースがほとんどです。
そして、その理由のほとんどは
面倒
という一言に集約できる気がします。
なので、プロジェクトにE2Eテストの導入を考える場合は
シンプル
ということを主眼に置くことをお勧めします。
最新のイケてるツールより、rubyやpythonでgetやpostを叩く方がE2Eテストは継続できます。
また、前の記事でも伝えたように
「このプロセスはそもそも必要なのか」
ということをよく考えて導入してください。
E2Eテストがなくても問題が発生していないプロジェクトであれば導入自体が不要です。E2Eテストは本当にコストがかかります。
次の記事では
local開発でhttpsを導入する
ということについて説明したいと思います。
Discussion