Humanity

Edit the world by your favorite way

Angular での単体テスト、E2E テスト、バックエンド無しの開発について

Jasmine による単体テスト

Karma (テストランナー) によるテスト実行の自動化

Jasmine 単体でもブラウザからテスト実行できるが、わざわざアクセスして確認するのが面倒な場合は Karma を使う。 コマンドからテスト実行できたりするので CI との相性が良い。

ngMock による疑似バックエンド処理

まだバックエンドが用意できていなかったり、AP サーバ立ち上げるより nginx などの Web サーバで静的ファイルだけ配信して開発を進めたいという場合。 GET だけなら JSON ファイルも Web サーバで配信させればいいですが、POST や PUT や DELETE となるとそうはいきません。 そういう場合に JSON Server というのもあるようですが、Angular だけで完結する方法として ngMock を使う方法があります。

Protractor による E2E テスト

Protractor は Selenium WebDriverJSベースの E2E テストフレームワーク

全体を通した話

それぞれの技術を導入した時の効果などについてまとまってる記事。

Angular で「Error: 10 $digest() iterations reached. Aborting!」というエラーが出る時の対処法

最近 Angular で調べたことを週末に記事にして上げるサイクルになりつつある。良い傾向。

あと説明する時につっかからずに説明できたりするので、自分で調べてまとめて人に教える重要さをひしひしと感じる… なので最近は「書かなくても分かるやろ」ってとこを手抜きせず、あえて言うなら冗長に書いたりしてる。

本題

Angular は監視(バインド)している変数の値が変更された場合*1、他の値が変わるか毎回ループを回してチェックする。これを digest loop と呼びます。

冒頭のエラーメッセージは digest loop を10回回したけど毎回値が変わっていて収束しなさそうだし、キリがないのでやめますという意味 (ざっくり)。

こんな場合に出る

対処法

いくつかあります。

  1. 関数ではなく変数にして $watch() により変更を伝播させる
  2. 違うインスタンスを返している場合は計算した結果を変数に保存してキャッシュしておき、キャッシュがあり再計算の必要がない場合はそれを返すなどして、同じインスタンスを返すようにする (バッドプラクティスなのでやるべきではない)。
  3. One-time binding を使う

良くある一例として、ng-repeat の in ... の部分では関数を使うべきではありません (1 のリンクを参照)。 もちろん関数で前述したようにキャッシュして常に同じインスタンスを返すようにすればエラーは起こらなくなるが、関数の中で無理矢理キャッシュして解決するのではなく、計算(関数)の終わりに計算結果を変数(モデル)にセットして、Angular 側に再計算させ変更を伝播させるようにすべきです。

つまり関数はキャッシュなどの不必要な副作用を持つべきではありません。 不必要というのは、変更を伝播させるために必ず計算結果を変数(モデル)にセットする必要があるからです。

キャッシュを使ってもべき等性(何回実行しても同じ結果が返ってくること)が保たれるなら問題ありません。 しかしバグを引き起こしやすくなるのであまり使うべきではないです。 Angular アプリのパフォーマンスを改善したければ Chrome の Angular 用拡張機能を使ってボトルネックを見つけ出す方が正道です。

しかしランダム値を返すなど、どうしてもべき等性を仕様的に担保できない場合もあります。 そういう場合は3番の One-time binding が有効です。

要点を箇条書きにすると以下の通りです。

  • 関数はべき等にする
  • 変更の伝播は Angular にやらせる
  • 関数がランダム値を返すなどしてべき等性を保てないなら上で挙げた対処法を試す

所感

React 使ってる人からしたら結論は当たり前だと思うだろうし Angular 1 こわいと思われそう… (だと思ってるけど実際どうなんだろう)

実際自分も Angular 1 の digest loop は度々悩まされてるので、digest loop の無いフレームワーク使えたらなぁと思う。 というかそもそも digest loop が双方向バインディングを実現する(ちょっと無理矢理な)方法なので、一方向にして Flux アーキテクチャを導入できたらとは思う。

ただ、React だけだと Flux の強制力はないので、またさらに他のフレームワークが必要となって、そうすると学習コストとかで敬遠される確率が高まる (実際は Angular のが面倒難しくても)。 あとフルスタックな技術は検索しやすい。

そういう意味でフルスタックという点では Java と同じく多いに助かってます。 業務寄りではないけど業務向けな感じ*2フレームワークという印象。

参考リンク

*1:例えばユーザーからの入力などによって

*2:本当にギョームな現場だったら未だに Angular というか SPA という選択肢はないだろうな…と思ってるだけ(偏見)

人は天啓を得ないと一日以上に渡る自己学習をすることは難しい

気がする。

例えば「セキュリティのこと勉強しよう!」とか「数学を勉強しなおそう!」とか思って継続的に続くことってあんまりない。 この例は毎日の仕事や趣味とかでやってることと全く違うことだから尚更なんだけど。 だから数時間でできることを継続的にやれるようなスケジューリングとか試みを考えるべき、というまぁなんか当たり前の結論になった。 正直これまでの自分は割と意地になって頭でっかちに根詰めるタイプだったので違うやり方を考えたいなーと思っている。 ただプログラミングに関しては割とのんびりやってこれたので、まぁ興味の赴くままにやりたいことやればいいんじゃねってことになる (プログラミングって言うと範囲広いけど)。 もちろん仕事じゃないなら別にそれでいいんだけど、なんか他に自分をうまくコントロールできる頭の良さそうな方法ないかなーみたいな淡い期待を抱いてなんとなく考えたりしてみてる (この文章がすでに頭悪そうなことは置いておく)。

数時間でできるとはちょっと違うけど、なんとなく印象に残ってるのが誰かの記事で毎日1つのサイトを作るってやつだった。 単純なのでもいいので、必ず毎日作ると書いてあったけど、今考えると「それ残業とか緊急対応しない会社・仕事じゃないと難しいな…」という感想しかなかった。かなしい。 確か外国の翻訳記事だったので、なんとなく残業はない前提なんだろうな…とか。

まぁさすがに1日は無理でも1週間だったら土日に作れるので*1、やっぱスケジューリング大事だなー (とは分かってはいるけどノウハウがない…)。 色んな人に継続的に続けてる習慣を聞いてみたい。

*1:幸い今の現場ではまだ休日出勤はしてない