はじめに
僕が開発リーダーとして担当しているプロダクトには
テストコードがあるもののメンテナンスされておらず、実質ないような状態です。
そして、そのままリリースを迎え、現在も(テストコードがないまま)保守開発が続いています。
そのため、
毎月入る仕様変更や追加機能開発時には、
リグレッションテストをまぁまぁのケース手で実行してからリリースしていたり、
製造してもテストコードは書かず結合テストを手動で実施して、何かあればそこで分かる(もちろん単体レベルのバグも出る)
という感じです。
この現状とプロダクトの特徴から、
テストという観点で何から着手するのが品質をコスパ良く高めるのに効率が良いか??について僕の中でのベストプラクティスをまとめてみました。
テストコードを書かないままリリースを迎えたプロダクトの概要
プロダクトの全体像を関連システムも合わせてざっくり絵にしたものがこれになります。
夜間処理でバッチ集計システムが諸々集計処理を行いデータベースを作成し、
日中、オンラインシステムであるダッシュボードシステムを利用してユーザは集計処理結果を参照するようなイメージです。
今回はダッシュボードシステムのバックエンド部分(Rest API)に対するテストについて考えました。
テストコードを書かないままリリースを迎えた原因
正直、僕が新規プロダクト開発に参画してすぐテストコード作成の方針や開発のルール(仕様変更が入ったらまずテストコードを修正して・・とか)を整備できれば
こんな事態になってなかったというのが一番の原因なのですが。。。
(僕が参画した際に、性能要件全く満たせないモックみたいな「何か」をお金かけまくって作ってしまっていて、おまけに全体アーキテクトが入れ違いにいなくなって、、、みたいなところで全部丸投げされてスタートして。。。優先度高い課題が山積みで、、、そんなん無理。。。。って言い訳ですね)
テストコードを書くにあたってのハードルは2つあり、
それがテストコードが最後まで書けなかったり、メンテナンスできなかった原因だと考えています。
テストの事前作業としてテストデータ登録済みのテーブル準備が必要
複雑な処理は夜間バッチでの集計処理で、バックエンドはあまり複雑にならないような作りをしていたので(ちょっとだけ複雑なクエリを投げたりするくらい)、
クリーンアーキテクチャを採用したりなどもせず、
O/Rマッパーでデータベースから期待通りselectして、レスポンスのフォーマットに加工できているか?というのがテストの主な観点でした。
そのため、テストの際にはテストデータが入ったテーブルをいつも同じ状態でパッと再現したり、テストの観点追加に応じて追加のテストデータをサクッと増やしたり(それ以降追加された状態)が必要になります。
このように、
テストコードに必要なテストデータを再現する仕組みと、最新のテストデータを管理するルールが事前に整理できていなかったのが原因の1つだと考えています
認可サーバとの連携
APIはspring securityを利用してメイン処理の前段で認可サーバと連携し、
アクセストークンは有効か??アクセストークンを送ったユーザ(セッション情報)が何か??
という情報を持っておいてその情報からそのユーザが持っている参照権限をチェックして、参照可能なレスポンスを返します。(権限詐称などさせないために)
そのため、
認証済みユーザ(セッション情報)をモック化してテストする必要があり、少しだけ手間がかかります。
こっちはちょっと調べればできる話ですが、、、一応これも原因の1つと考えています。
テストコードを書くためにまず着手したこと!!現状に対する僕が考えたベストプラクティス!?
今の状況とプロダクトの特徴から、
まず着手すべきことについて考え、出した結論が以下になります。
テストコードの目的を開発メンバーに理解してもらう
すでにリリースを迎えたプロダクトに今更テストコードを書く目的として、以下を掲げまずなぜテストコードを書かなければならないのか??
逆に書かなければどうなるのか??ということを納得してもらうことから始めました。
「仕様変更、リファクタリングなどの際、今まで動いていた機能が今まで通り動くことの確認を開発しながら何度でも確認できる。
それによってバグを早い段階で検知でき、仕様変更、リファクタリングを積極的に行えプロダクトの品質担保だけでなく機能性の向上にも繋げられる。
だから今からテストコードを書きましょう!!!」
flywayを利用してテスト事前データ準備のハードルを下げ、テストデータの管理を実施
ローカル環境でdocker上にDBを立て、
事前にテーブル定義、データ登録のクエリを作成しておき、flywayでテストデータをセットアップできるようにしました。
また、
テーブル定義、データ登録のクエリはプロダクションコード、テストコードと同じくgithubで管理することで、
テストデータ、テスト期待値、ロジックを最新で常に管理でき、ローカル環境ではflywayを利用してサクッとテストデータを準備できるようになりました。
これにより、
テストコード作成、実施、テストコードやテストデータのメンテナンスのハードルは下がったと思います。
(具体的なテストデータは今あるステージング環境のDBからcreate、insertなどのクエリを自動生成して準備しました)
サンプルコードやflywayの使い方のポイントなどなどはこちらの記事にまとめました。
flywayでテストデータ投入|spring bootで作成したAPIのテストコードのためのデータ準備
ログインユーザのセッション情報モック化
テストコードに独自で作成したセッション情報をセットする処理に紐付けて、
独自のアノテーションでログイン中のユーザ情報をセットした状態でテストが実行できる仕組みを作りました。
これによって認可サーバへの依存なしでテスト実行できるようになりテストコード作成のハードルは下がったと思います。
こんな感じです
@Test
@MyTestUser(loginUser = "sample001")
public void TextSample() throws Exception {
この仕組みの具体的な手順はまた別の記事で紹介します。
一先ずAPIの最初から最後までを通すようなテストコードの正常系のみをやや単体チックに書くところから始めてみる
テストコードを書くための準備を整えたところで、
何から着手するかを考えた結果、このような方針で進めることにしました。
APIの最初から最後までを通す内部結合(ITa)に近いテストから作成する
現状テストコードが全くないので、
できるだけ早く、全てのAPIで、広い範囲をカバーできるテストコードを作成し、直近の仕様変更などに備えるのが目的です。
テストコードは正常系のみでやや単体チックに書く
すでにリリース済みで過去に単体テスト済みというのもありますが、
個人的には細かい異常系、境界値、最大値、最小値などなどのテストを何ケースも書いたり、カバレッジを上げるのにこだわるのではなく、
正常系1ケースで、例えば検索系の場合は全項目assert、合計件数assertくらいからが落としどころかなぁと感覚的に思います。
本来なら、単体テストを別途用意すべきでしょうが、、、、何から始めるか??目的は何か??を考慮してこのような方針にしました。
まずはこの辺りから始めて、
仕様変更などによりもっと細かい単位でのテストが必要になればそれに応じてAPIの最初から最後までではなく、
もっと細かい単位でテストを書いていき、仕様変更のたびにテストコードでカバーできる範囲がもっと細かくなっていけばいいかなぁと思います。
仕様変更の際はテストコードを先にメンテナンスする運用ルールとする
テストコードのメンテナンスを意識してもらうためと、
テストコードの期待値を先に変更して、
テストNGになるところから→仕様変更してOKになり→さらにリファクタしてOKのままになる
ということ実際に体験し、テストコードがあり、何度も手軽に実行できることがこれだけ大事なのだということを開発メンバーに体感してもらうことが目的です。
まとめ
今回はテストコードがないままリリースを迎えたプロダクトに対して何から着手するか??をテストの観点から考えてみた内容をまとめてみました。
まず以下を実施することから始めましょう!!というのが僕の結論です。
- テストコードの目的を開発メンバーに理解してもらう
- flywayを利用してテスト事前データ準備のハードルを下げ、テストデータの管理を実施
- ログインユーザのセッション情報モック化
- 一先ずAPIの最初から最後までを通すようなテストコードの正常系のみをやや単体チックに書くところから始めてみる
もし、僕と同じような状況で、
なんとかしなければ。。。でも何からやればいいか??という方の参考になれば幸いです。
逆に、普通にいつもテストコード書いている方や、
テストの時にはDBでテストデータ用意しなくてもいいように素敵な仕組みを作られている方
などからすると、、、ツッコミどころ満載なんだろうなぁー。。。。って思いますが
2022/3/22追記
現場での開発を想定した
バックエンドのローカル開発環境構築まとめ記事を作ったので
良ければそちらを参考に環境構築してみてください!!
IntelliJ IDEA、docker desktop(postgreSQL、keycloak)、Flyway、DBeaverを利用したバックエンド開発環境構築