チェ・ゲバムラの日記

脱犬の道を目指す男のブログ

【AWS】2章ハッシュイベントによるビューのルーティング~サーバーレスシングルページアプリケーション(OREILLY本)まとめ~

この章でわかること

・Jasmineを使ったテスト

従来のMVC WEBフレームワークはサーバーサイドルータを使用してURLをコントローラに対応づけ、ビューとモデルで動的コンテンツを生成していた。
シングルページアプリケーションではクライアントサイドルータを作成し、サーバーへのリクエスト往復回数を取り除いて更に改善する。
結局、WEBはドキュメント間をハイパーリンクで繋いでいるが、URLにハッシュタグを含めるとページはリロードしない。(#homeなど。URLハッシュと呼ぶ。)

Javascriptを使うとハッシュ変更時にブラウザがトリガーするイベントをリッスンできることを利用して、ユーザーに見せたいマークアップ(ビュー)を作成できる。

2.1 テストしやすいルータを設計する

AWSサービスは基本的にローカルマシンでは動かない。
テストファーストで設計する為、ルータ設計から手を付ける。
全体のテストではなく、小さな振る舞いのテスト(数ミリ秒で実行できる)を作成する。

2.1.1 Jasmineテストを実行する

テストを書くのにJasmineテスティングフレームワークを使う。
Jasmine 公式
https://jasmine.github.io/

ブラウザからアクセスしてテストを実行
http://learnjs.hogehoge.com.s3-website-us-east-1.amazonaws.com/tests/index.html

※バグを報告した人がいてバグを再現出来ない時、報告者に対してテストにアクセスしてもらうように依頼する。
デプロイ時にはテストもデプロイされるため、本番環境でテストを実行してもらい、自分ではそのテストを再現すれば良い。

Jasmineでは、describe、it という2つの関数に渡すコールバックにテストを書くことでテストをまとめることができる。

2.1.2 最初のテストを書く

describe関数とit関数に与えたテキストを組み合わせてテスト内容を表すセンテンスを作成する。
it関数のit自体が、テスト名の付け方のヒントとなる。
テスト本体ではルータ関数(Javascript関数)を実行する。
ルータ関数の仕事はURLハッシュで定義したら適切なビューを返すこと。

ここではshowViewという関数をlearnjsという名前空間(namespace)に置く。
今回はハッシュ値を「#problem-1」として渡す。

./public/tests/app_spec.js
>|javascript|
describe('LearnJS', function(){
it('can show a problem view', function(){ //テスト名
learnjs.showView('#problem-1');
expect($('.view-container .problem-view').length).toEqual(1);
});
});
|


この時点でデプロイして読み込んでみると、HTMLにクラスを追加していないので、当然失敗する。

2.2 ルータ関数

2.2.1 名前空間を作成する

showViewをそのまま定義することも可能だが、名前が衝突しないように名前空間を作る。

public/app.jsに記載。

'use strict';
var learnjs = {};

これだけ!
関数と変数はこのオブジェクト内に追加していく。

2.2.2 ルータ関数を追加する

TypeError: learnjs.showView is not a function
→learnjs.showViewを作っていない為、関数ではないというエラー。

public/app.jsに追加

'use strict';
var learnjs = {};
learnjs.showView = function(hash){
        var problemView = $('<div class="problem-view">').text('Coming soon!');
        $('.view-container').empty().append(problemView);
}


再びデプロイするとまたメッセージが変わる。
Expected 0 to equal 1.
JavaScriptのエラーではなくなり、コード自体は正常に動いている。
app_spec.jsが動いていてview-containerがないため追加する必要がある。

pubic/app.jsに追加修正

 17     <div class="markup">
 18       <div class="view-container container">
 19           <div class="row">
 20             <div class="one-half column">
 21               <h3>Learn javascript</h3>
 22               <a class="button button-primary" href="">start now</a>
 23             </div>
 24             <div class="one-half column">
 25               <img src="/images/HeroImage.jpg">
 26             </div>
 27           </div>
 28       </div>
 29     </div>


17行目のmarkupクラスを追加すると、public/tests/SpecHelper.jsの機能が働いて、public/index.htmlの内容をテストしてくれるようになる。
※通常はtests/index.htmlのみチェック。
18行目のview-containerクラスも追加することでテストは成功する。

2.3 ルートを追加する

ルート(route)とは
ハッシュとビュー関数を関連付けること。
routesオブジェクトにエントリーを追加することでビューを追加できる。

ヌルケース(デフォルトケース)
→いわゆるNULLではなく、空文字列や0などという意味。
ハッシュがない場合にデフォルトページを表示する場合に使われる言葉。

public/app.js

'use strict';
var learnjs = {};

learnjs.problemView = function(){
  return $('<div class="problem-view">').text('Coming soon!');
}

learnjs.showView = function(hash){
  var routes = {
    '#problem-1':learnjs.problemView
  };
  var viewFn = routes[hash];
  if(viewFn){
    $('.view-container').empty().append( viewFn() );
  }
}

ランディングページビューをlanding-viewクラスをもつ

にいれて、アプリケーションとテストはランディングページビューを選択できるようになる。

2.4 ビューパラメータを追加する

今までの作り方ではアプリケーション拡張のたびにルート(ビュー)を作る必要があるため、パラメータ化してビューを組み立てる。
ハッシュを名前とパラメータにハイフンをいれて分割する(ビューパラメータ)

テストにはスパイを使うと、コードの2つの部分間の相互作用をテストできる。

2.4.1 スパイを使って相互作用をテストする

スパイはテストダブルの一種。
実際のオブジェクトや関数の代役をつとめてテストをサポートする。

Jasmineのspy関数を使う。
→指定された関数を一時的にスパイに置き換える。
スパイは自分に対する呼び出しを全て記録する。
Jasmineのマッチャーを使うとスパイが特定の引数で呼び出された事を確認できる。
テスト終了後、スパイはもとの関数に置き換わる。

tests/app_spec.js

describe('LearnJS', function(){
  it('can show a problem view', function(){ //テスト名
?-  learnjs.showView('#problem-1');
?-  expect($('.view-container .problem-view').length).toEqual(1);
  });

  it('shows the landing page view when there is no hash',function(){
    learnjs.showView('');
    expect($('.view-container .landing-view').length).toEqual(1);
  });

  it('passes the hash view parameter to the view function',function(){
    spyOn(learnjs, 'problemView');//第二引数はスパイする関数を含むオブジェクト、
第二引数は関数名
    learnjs.showView('#problem-42');
    expect(learnjs.problemView).toHaveBeenCalledWidth('42');
  });
});


うまくいってないのでまた更新予定