こんにちは、オイシックス・ラ・大地エンジニアの川上です。
今年の10月にオイラ大地にJoinした新参者ですが、Oisixシステムのマイクロサービス化に携わっております。
Oisixではマイクロサービスを構築する上でのアプリケーションフレームワークにSpring Bootを採用しておりますが、今回はテストの手法にフォーカスして取り組みをご紹介したいと思います。
テストの重要性
システム開発を行う上では、テストについて考えることを避けて通ることはできません。
ソフトウェアの第三者検証サービスを提供するなど、テストを専門とする企業もあるほど。
Oisixでもシステムトラブルでお客様にご迷惑をおかけすることの無いよう、安定したサービスをご提供するためにリリース前には入念なテストを行なっています。
しかしながら、新しいサービス、あるいは問題点の改善をいち早くお客様にご提供するためにはテストにもスピードが求められます。
システムのマイクロサービス化を行う上で、品質を保ちながらもデリバリースピードを上げていくというのは重要なテーマの一つとなっています。
Continuous Integration
システム開発の世界ではお馴染みのContinuous Integration(以後CI)ですが、Oisixシステムの開発においても仕組みの整備・改善に取り組んでいます。
コードの変更に合わせて継続的にテストを行うことでバグの混入をすばやく検知し、プロダクトコードの品質を高く保つことができます。
OisixではJavaの代表的なフレームワークであるSpring Bootによる開発を行っていますが、Javaのテストフレームワークと言えばJUnitですよね。
OisixでもJUnitのテストコードを日々実装しています。
継続的に実施するテストコードに対して求められるもの(テストを構築するコツと言っても良いかもしれません)については、多くの技術者の方が議論、あるいは研究されていることかと思います。
個人的な見解を多分に含みますが、品質とスピードを両立する上で重要だと感じているポイントを大きく二点ほど挙げさせていただきます。
何度でも繰り返しテストができること(冪等性)
CIを回す上においてテストコードはプロダクトコードに変更が加えられる都度実行されるため、何度も繰り返しテストを実行できる必要があります。
難しい言葉になってしまいますが冪等であることが求められます。
繰り返しテストを行う中で、特定のタイミングで実行すると失敗するようなテストが含まれていたり、連続して実行すると失敗するようなテストが含まれているとCIに対する信頼が損なわれて(テストが失敗することを異常だと思わなくなって)しまいます。それではせっかく書いたテストコードも宝の持ち腐れになってしまいますよね。
テストコードが担保してくれる範囲については信頼でき、リリースに向けた他のポイントに注力するほうが健全で生産的な状態です。
同時にテストができること(並列性)
複数の開発案件が輻輳する場合など、多くの開発者が平行してテストを実行するようなケースもあるでしょう。その場合、共用しているテスト環境の順番待ちで時間を無駄に過ごしてしまったり、あるいは同時にテストを実行しようとして失敗する、といった経験をされた方もいることでしょう。
CIを効率的に回していくためには、テストを並列して実行可能な環境を作り上げることが重要になってきます。
実現するためのポイント
冪等性と並列性を兼ね備えたテストを構築するにはどうすればよいか?
繰り返し実行可能なテストコードを書く(状態の初期化・設定をしっかり実装する)ことは前提として、ここではプラスアルファのポイントをご紹介します。
テストを実行する環境をコンテナ化する
CircleCIやGitLab CIなど、多くのCIツールがサポートするようになってきていますが、Dockerコンテナ上でテストを実行することで、テストの実行ごとに専有できる使い捨ての環境を割り当てる事ができます。
この方法を取ることでテストの実行ごとに環境差異が発生してしまうようなことや、環境の競合などを避けることができます。
アプリケーションがアクセスするリソースをコンテナ化する
この方法もCIツールでサポートしてくれることが多いですが、データベースやKVSなど、アプリケーションを実行する際にアクセスが必要なリソースをコンテナ化しておくととても便利です。
様々なミドルウェアの公式Dockerイメージが配布されているため、プロダクション環境ではマネージド・サービスを利用するようなリソースについても、テスト実行時にはコンテナを活用することができます。
OisixではJavaのフレームワークであるSpring Bootを利用していることから、JavaのテストコードからDockerコンテナを起動できるTestcontainersというテストフレームワークを活用し始めています。
Introduction · Testcontainers
アプリケーションからアクセスするリソースのコンテナはCIツールの機能を利用してサイドカー的に起動することが多いですが、このフレームワークを使用してテストコード内でコンテナを起動することができます。
そのため、開発者のローカルPCにおいてテストコードを実行する場合でも、Dockerさえインストールされていればリソースを用意することができます。
どうしても用意できないものはMockライブラリを活用する
Dockerコンテナで代替の効かないマネージド・サービス(ストレージやメッセージングサービスなど)へのアクセスなどはMockライブラリを活用するのも一つの方法です。 Spring Bootが提供するBean定義の仕組みによって、特定のクラスをMockに差し替えて実行すると言ったこともテストコード上で簡単に実装することができます。
常に改善し続けること
この記事ではJUnitテストにまつわる取り組みについて一端をご紹介しましたが、システムが一度作って終わりではないのと同様にテストも一度実行して終わりではありません。
当然のことながらOisixでも日々改善を模索しています。
Spring Bootが提供する豊富なテスト支援機能の有効活用や、CIとくれば併せて考えたいCD(Continuous Delivery)手法の検討など、改善のテーマはたくさんあります。
私も試用期間中の身ではありますが、テスト手法に限らず積極的に提案を行い、改善に取り組んでおります。また、新参者の提案でもきちんと聞いてくれるOisixのエンジニアチームもとても魅力あるチームだと感じています。