レガシーな Angular にテストを導入して幸せになりたい話

こんにちは!アドウェイズ21新卒入社、アプリケーションエンジニアの梅村です。

私の所属しているマーケティングテクノロジー Div STROBELIGHTS U では昨年からレガシーマイグレーションを進めています。

今回はその取り組みの中から、広告運用担当者向けの社内管理画面のフロントエンド (Angular) にユニットテストを導入しようと奮闘している話を書きたいと思います。

内容としては、レガシーマイグレーションの概要、Angularにテストを導入することになった経緯、技術選定、環境構築あたりになります。

実際のテストスイート等の話はここではいたしませんので悪しからず。

想定読者

主に新卒1年目に取り組んだ活動とチームで進めているレガシーマイグレーションの紹介になるので以下のような方にオススメです。

  • 新卒1~2年目のエンジニア
  • エンジニアになりたい就活生
  • レガシーマイグレーションに興味がある人

レガシーマイグレーション

社内向けのシステムということもあり、システムの構成要素のアップデートが滞りがちになっていました。

中には、かなり古いバージョンを使用しているものもあり、セキュリティの面でもメンテナンスの面でも問題があることからチームでレガシーマイグレーションに取り組むことになりました。

レガシーマイグレーションの主な内容としては大きく分けて以下の2つです。

  1. Ruby と Rails のバージョンアップ
  2. AngularJS から Angular への移行

本記事の主題であるフロントエンドへのテスト導入は2に関連する内容になりますので、こちらについてもう少し掘り下げたいと思います。

1についてはいずれチームメンバーの誰かがブログにしてくれると思いますので楽しみにお待ち下さい。

AngularJS から Angular への移行

フロントにAngularJSを採用したものとAngularを採用したものの2種類のRailsエンジンが存在しており、機能によってどちらのエンジンに乗っているか異なるという状況でした。

AngularJS は Angular の前身ではありますが、使用言語がJavaScript と TypeScript で異なるためほぼ別物であり、混在していることでメンテナビリティが低下します。

昨年末にサポートが終了しており、バージョンアップするためには後継であるAngularに移行するしかありません。

また、Angular についてもバージョンが古く、最新版からかなり後れてしまっています。

そこで、まずは AngularJS で作られたエンジンに乗っている機能を Angular で書かれたエンジンへと移行し、移行完了後に Angular のアップデートを行うという方針を取ることになりました。

移行作業は週1回の改善デー(システムの改善活動に集中する日)にモブプログラミングで行っています。

改善デーについて、詳細は過去記事を御覧ください!

テスト導入の経緯

Angular移行を行うにあたって、私にはやりたいことがありました。
もちろんテストの追加です。

とは言っても、当初はフロントについては全く考えておらず、Rails のテストの欠落している部分を追加することを考えていました。

フロントエンドのテストいえばE2Eテストのイメージが強く、導入コストが高いと感じていたためです。

しかし、移行作業を進めていくにつれてフロントのテストの必要性を感じるようになっていきました。

テストを書くことでフロントでのデータの取り扱いやバリデーションなどの見落としを防ぐことができます。

何より今後行うAngularのバージョンアップのためにもフロントエンドのテストがあったほうが安心です。

そこでフロントのテストについて改めて調べてみると、バックエンドと同じようにユニットテストを手軽に書けることを知りました。

そうと分かればやることは1つです。

私 「フロントエンドのユニットテストがあるらしいのでやってみようと思います。」

上司「いいですね!梅村さん主導でやりますか?」

私 「とりあえず何使うかとか一人で考えてみたいです!」

上司「了解しました!何かあったら声かけてください。」

かくして、Angular のテスト導入を検討段階からやらせてもらうことになりました。

やりたいことを積極的に後押ししてサポートもらえる環境はありがたいなと思いますし、ADWAYSの良いところだと思います。

Angular のテスト導入

Angularのテストの導入にあたっては、上司と相談して以下のようなフローで進めることにしました。

  1. 調査
  2. 技術選定
  3. 環境構築
  4. 資料作成
  5. チーム内共有
  6. モブプロでテスト追加

本記事では主に2と3について書いていきます。

管理画面のAngularについて

技術選定の話をする前に、今回のテスト導入の最も大きな障害となった管理画面のAngularについて書いておきたいと思います。

先述の通り、使用しているAngularはバージョンが古く、v2です。

AngularはAngularJSをv1として数えているため、実質1番最初のバージョンになります。

最新のAngularがv13で、長期サポート期間に含まれるのがv11~v12であることを踏まえるとAngular v2で作られたシステムのレガシー加減がお分かりいただけるかと思います。

更に悪いことに、Angular CLIが導入されていません。

Angular CLIにはAngularプロジェクトの作成・コンポーネントやサービスの作成・コンパイル・ビルド・プロジェクトで使用するAngularのバージョンアップなど様々な機能があります。

もちろんテスト機能もついているため、Angular CLIさえ導入されていれば技術選定も環境構築も必要ありませんし、この記事を書くこともなかったのではないかと思います。

Angular CLIの導入も検討しましたが、そもそもAngularプロジェクトはCLIを使って作られている前提であり既存プロジェクトへの導入に関する情報が少ないことや、CLI導入用のコマンドが廃止されていることなどからコストが高く、今回はテスト環境を自前で用意することにしました。

ただし、CLIがないと何かと不便なため、今後CLI導入に向けた調査も進めていければ良いなと思っています。

技術選定

結論から先に言いますと、Angular CLIで採用されているKarmaとJasmineを採用することにしました。

動作が高速で最近流行りのJestを使ってみたい気持ちはすごくあったのですが、Jestがリニューアルして注目を集め出したのが比較的最近なため、レガシーなAngularに関する情報があまり見つからず断念しました。

KarmaはもともとAngularJSのテスト用に開発されたテストランナーで、テストフレームワークやテスト結果の表示形式など様々な設定をカスタマイズすることができます。

テストフレームワーク単体でもテストの実行は可能ですが、Karmaを入れることでCIへの登録などの拡張も行うことができて便利です。

公式のお墨付きの安心感もありますが、何より今後Angular CLIを導入した場合にも設定をそのまま使える点が魅力的でした。

テストフレームワークについては種類も多く何を採用すべきかかなり悩みました。

選定においては、主に以下の項目を大切にしました。

  • 多くの人に使われていて情報量が多いこと
  • 継続的にメンテナンスされていること
  • アサーションやモックなどある程度の操作ができること
  • チームの学習コストが低いこと

最初に1つ目と2つ目の条件を満たすものを探すためにTypeScriptのテストフレームワークのトレンドを調べ、その中から3つ目と4つ目の条件に合致するものを探しました。

JasmineはBDD(Behavior Driven Development / 振る舞い駆動開発)であり、Railsのテストで用いているRSpecと記法が類似していることが決め手となりました。

また、Jasmineの記法はJestと類似しているらしいので、万が一テストの実行速度に不満が募ってJestに乗り換える事態になっても、比較的移行しやすいのではないかという下心も正直少しあります。

環境構築

Karma+Jasmineの基本的な環境構築方法についてはKarmaの公式サイトにも記載されていますし、優れた記事もたくさんありますので他に譲ります。

ここでは、デフォルト以外の設定を使用した箇所を中心に書いていきたいと思います。

以下項目については、Karmaの設定ファイルである karma.conf.js の設定項目になります。

ちなみにこのファイルは .ts.coffee 形式で記述することが可能です。

preprocessors

文字通り前処理を行うためのオプションです。

AngularはTypeScriptで書かれているため、JavaScriptにトランスパイルする必要があります。

単にTypeScriptをトランスパイルするだけなら karma-typescript-preprocessor を使っても良いのですが、モジュールの import ができません。

そこで今回は karma-webpack を使います。 webpack の設定は以下のように書くことができます。

webpack : { 
  devtool : ' inline-source-map ' 
}

ここでは、ソースマップを生成させるツールを追加しました。

既存の webpack.config.js を読み込んで設定を流用することもできますし、新たに設定を追加することもできます。

browsers

Karmaは起動後にブラウザからアクセスすることでテストが実行されます。

ブラウザへ自動アクセスするためのランチャーの設定を行うことができます。

GUI表示は特に必要なかったため ChromeHeadless を使用しました。

また、 --no-sandbox オプションがないとChromeが落ちてしまうため以下のように customLaunchers の設定を追記する必要があります。

customLaunchers: {
    ChromeHeadlessCI: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
    }
}

reporters

テスト結果の出力を好きなものに変更できます。

今回は mocha を使用しました。

このあたりは完全に趣味の世界なのでチーム内で使いやすいものを選ぶのが良いかと思います。

個人的にももう少し調査してみたい箇所です。

感想

環境構築は他にもたくさん躓いたところがありますが、公式のドキュメントがそれなりにしっかりしているので比較的理解しやすい印象です。

ただ、上で挙げたランチャーやプリプロセッサなど外部のプラグインを使用する場合、それぞれのドキュメントを見て設定しなければならない点と、エラーが出た場合に問題の切り分けがしづらい点が作業していてとても辛かったです。

今後の展望

今後の展望としては、まずテスト導入について得た知識をチーム内に展開します。

自分の担当プロダクト以外でもフロントエンドのテスト導入を前向きに検討してもらえるよう尽力していきたいです。

そして、担当プロダクトについては移行作業対象箇所へのモブプロでのテスト追加と既存コードへのテスト追加を行っていきます。

git push 時にテストが実行されるようにGitHubおよびCircleCIとの連携設定も行う予定です。

フロント部分のレガシーマイグレーションの最終目的としてはAngularのバージョンアップを行うことなので、そのためにもテストカバレッジを向上させていきたいです。

まとめ

今回はチームで取り組んでいるレガシーマイグレーションの概要と、Angular v2にCLIを使わずにKarma+Jasmineのテスト環境を構築するまでの話について記事を書きました。

苦労話やどのように考えて動いたかという部分がメインになってしまい、技術的な解決を求めていた方には物足りない内容だったかもしれませんが、レガシーマイグレーションに取り組みたいもしくは取り組んでいるという同士の方のお力に少しでもなれていれば幸いです。

また、テスト導入については今年の1月頃から通常業務の合間を縫って2h/週ぐらいずつ地道に進めてきた内容になります。

通常業務で精一杯、まだ1年目だしやれないかもなどなど不安なことはたくさんありましたが、自分が問題意識を持って変えていきたいと思ったことを実行に移すことができて本当に良かったと思います。