こんにちは。サーバーエンジニアの川島です。
今回は最近チーム向けに共有した、APIのユニットテストを書くときのガイドラインについて、考え方などを少し紹介したいと思います。
背景
ガイドラインを作ることになった背景として、以下のような要因がありました。
- ユニットテストのガイドラインがないため、テストの実装方針が担当者によって差が生まれている
- そのため、コードの意図を汲み取りづらくコードリーディングに時間がかかる
- ユニットテストのガイドラインがないため、新規でテストを実装する際にどのように実装していいか考える時間が多くなる
- 既存のアーキテクチャを流用するか、1から自分で書いてしまうか悩んでしまうことがある
- 似ているテストを流用する際、既存のあまり良くないテストをそのまま使い回してしまうため、良くないテストが量産される
これらの問題の解決を図るため、APIユニットテストのガイドラインを作成することにしました。
目的
ガイドラインを作成することで、以下のような効果を期待しています。
- APIユニットテストの実装の粒度をできる限り統一したい
- テストコードの実装を揃えることで、コードリーディングにかかる時間の短縮を図りたい
- コードの粒度が統一されることで、API開発者がこれまでよりも素早くテストを実装できることを目指す
ガイドラインの内容(抜粋)
ここからは具体的な方法について触れていきます。
テストファイル
- テストは可能な限りシンプルに保つようにする
- 1つのテストファイルには1機能のテストを書く(複数の機能や状態をチェックしない)
- テストケースが膨大になるものは分けることを検討する(1000行を超える場合など)
1機能の中で確認したい内容が多い場合などはケースごとに分割します。
一例として、以下のような形をとっています。
- tests
- api
- api_function_dir
- fixtures
- テストで利用するデータ群
- cases
- ケース分割されたテスト群
- APIのユニットテスト群
* api_function_dir: 各APIをカテゴリ化したディレクトリ。基本的なテストはこのディレクトリで実装します。
テストコードの実装
テストメソッドの宣言
- 何をテストしているか、コメントでわかるように書く
- メソッドの最初に当該テストでは何を確認しているかを書いておく
- ぱっと見てわかりやすいケース名をメソッド名にする
キャラクターのレベルアップを検証するテストを例にとります。
以下はわかりにくい例です。
実装を見ることで何をしているのか推測できますが、メソッド名からは判断できません。
適切にコメントが書かれていない場合はさらに理解に時間を要します。
def test_level_up_p1():
# 具体的なテスト
def test_level_up_p2():
# 具体的なテスト
以下は改善例です。
パターンの書き方については分割するか、フレームワークによっては1メソッドで複数パターンを検証できるものがあります。
その場合は汎用的なケース名にしておくとよいでしょう。
また、クラスを分けるなどして読みやすくすることもできます。
def test_success_level_up():
# 成功パターンのテストを実装
def test_failure_level_up():
# エラーパターンのテストを実装
メソッドの振る舞い
- 他のテストメソッドを呼び出さない
他のテストメソッドを汎用的に使いまわしてしまうと、調査や改修をする際にテストの影響範囲を確認する必要が出てきます。
テストの影響範囲はメソッドのスコープ内で収まるように実装します。
APIレスポンスの検証
- APIレスポンスは確認したい値のみ検証する
既存のユニットテストでは実装者によって複数の検証方法が混在しています。
その中の一つとして、APIレスポンスのJSONデータそのものを検証していることがあります。
例えば、このようなレスポンスが返ってくるAPIがあるとします。
expect_json = {
'status_code': 200,
'value': 'test'
}
response = call_api() # JSONで返ってくる
assert expect_json == response # JSONそのものを検証しているためテストの詳細がわからない
このようなテストでは何をテストしたいかわからないことが多いです。
また、JSONの構造に変更があった場合は、同様の方式で検証している全てのテストが失敗しますので、精神衛生上よろしくありません。
そのため、確認したい値のみの検証を行うようにします。
expect_json = {
'status_code': 200,
'value': 'test'
}
response = call_api()
assert expect_json['status_code'] == response.status_code
assert expect_json['value'] == response.value
まとめ
作成したガイドラインの一部をご紹介しました。
考え方や手順を整備していくことで少しずつ改善していけると思いますので、それぞれのチームに合わせたガイドラインをぜひ作ってみてください。