Thursday, July 3, 2014

Testing backbone view using jasmine with Grunt and requirejs

「う、うご…動いた…よ…うん、動いたんじゃないかな? ニコリ…」

sinon.jsを使ってVIEWのテストを書いたけど、これ大変だっ.

Gruntfile.js

使いにくい…分かりにくい…初見殺し….

具体的に言うとgrunt-jasmine-contribとgrunt-template-jasmine-requirejsの組み合わせ.この2つのプラグインが作る_SpecRunner.htmlがどうなっているか逐一確認しながら設定しないといけないし、VIEWのテストはphantom.jsが動いている環境でないとできない.

Gruntfile.js - jasmineタスクの設定部分を抜粋

jasmine: {
    all: {
        src: '.tmp/scripts/{,*/}*.js',
        options: {
            keepRunner: true,
            specs: '.tmp/spec/*.js',
            template: require('grunt-template-jasmine-requirejs'),
            templateOptions: {
                requireConfigFile: '.tmp/scripts/main.js',
                requireConfig: {
                    baseUrl: '.tmp/scripts',
                    paths: {
                        "sinon": "../../bower_components/sinon/lib/sinon"
                    }
                }
            },
            vendor: [
                    "bower_components/jquery/dist/jquery.js",
                    "bower_components/jasmine-jquery/lib/jasmine-jquery.js"
            ]
        }
    }
},

もう色々間違っている気がする. どうしてこんな事に…

vendorの設定

Gruntfile.js jasmineタスクの設定項目にvendorを追加. Backbone.Viewのテストでfixtureのロードが必要なのでjasmine-jquery.jsを入れてある. 更にjasmine-jquery.jsが依存しているjquery.jsを追加するというrequire.js?何それ状態.

尚、ここにsinon.jsを含めると大惨事になる. 公式の人たちはbower_componentのsinonはオフィシャルじゃないから自分でビルドしろ(ソース)などと書いてあるけど流石にそれは勘弁…という事でrequireConfigのpathsに追加している. これだとうまくいく不思議.

テストコード

define [
  'underscore'
  'backbone',
  'sinon',
  'models/recipe',
  'views/home'
], (_, Backbone, sinon, RecipeModel, HomeView) ->

# (省略...) #

  describe "Yukaary backbone view test", ->

    view = null

    # If you activate that, it means our backbone with sion
    # will not publish actual http request. This can be preserved
    # communication errors when you open _SpecRunner.js on your brower,
    # but loading fixture is not working under this environment.
    #sinon.stub(XMLHttpRequest.prototype)

    beforeEach ->
      loadFixtures 'index.html'
      view = new HomeView()

    it "view is loaded", ->
      expect(view).toBeDefined()

    it "just debugging", ->
      # html() is better way to see which html(fixture) is loaded.
      #console.log $('body').html()
      #console.log view.template
      #console.log view.$el
      #console.log view.render().$el.html()
      #console.log view.tagName
      #console.log view.$el.context 

    it "should have a right class", ->
      expect(view.$el.selector).toBe('div#contents')

    it "should have a right text", ->
      expect(view.render().$el.find('a')).toHaveText("Become A Crafter!")

    it "should handle an event", ->
      # assign apy for the event.
      eventSpy = sinon.spy()
      view.on('create-crafter', eventSpy)

      # render a view to get target button.
      view.render()

      # now let's click a button.
      $('a#create-btn').click()

      expect(eventSpy.callCount).toBe(1)

前回のテストにVIEWのテストを追加してみた.

先頭defineのところでsinon.js(require.conigに追加したためorz)と今回新たにテストするクラス「HomeView」を追記している.

後は謎の英語コメントに書いている通り. テストっぽいのは最後の「should handle an event」くらい.

  • loadFixtureでメインページ(index.html)をロード
  • HomeViewをレンダリング. これがdic#contentsに展開される.
  • 展開したhtmlに含まれるa#create-btnリンクをクリック
  • イベントがいい感じに処理され、最終的にHomeViewでcreate-crafterイベントが発行される
  • create-crafterイベントを監視していたeventSpyにイベントが発行されたことを確認

今回は「eventSpyが1度呼ばれた」ことをcreate-crafterイベントが発行されたと解釈している. 他にも色々をやり方はあるみたい.

テスト実行

yukaarybox:yo-practice yukaary$ grunt test
>> Local Npm module "grunt-template-jasmine-requirejs" not found. Is it installed?

Running "test" task

Running "clean:server" (clean) task
Cleaning .tmp...OK

Running "coffee:dist" (coffee) task

Running "coffee:test" (coffee) task

Running "createDefaultTemplate" task

Running "jst:compile" (jst) task
File ".tmp/scripts/templates.js" created.

Running "compass:dist" (compass) task
directory .tmp/styles/ 
   create .tmp/styles/main.css (2.567s)
Compilation took 2.576s

Running "compass:server" (compass) task
unchanged app/styles/main.scss
Compilation took 0.18s

Running "connect:test" (connect) task
Started connect web server on http://localhost:9001

Running "jasmine:all" (jasmine) task
Testing jasmine specs via PhantomJS

 Yukaary's 1st test
   ✓ Maki san should dig tunnel.
   ✓ Zunko san shoud have zunda mochi.
 Yukaary 1st backbone model test
   ✓ depended components are loaded.
   ✓ sinon is available
 Yukaary backbone view test
   ✓ view is loaded
   ✓ just debugging
   ✓ should have a right class
   ✓ should have a right text
   ✓ should handle an event

9 specs in 0.474s.
>> 0 failures

Done, without errors.


Execution Time (2014-07-03 13:09:43 UTC)
loading tasks      2s  ▇▇▇▇▇▇▇▇▇▇ 21%
coffee:dist     141ms  ▇ 1%
compass:dist     3.6s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 38%
compass:server  464ms  ▇▇▇ 5%
jasmine:all      3.2s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 34%
Total 9.4s

やったぜ.

補足1

grunt-jasmine-contrib、一昔前にあった「grunt-jasmine-runner」を引き継いだものらしい.

Gruntfile.jsには一切出てこないけど「$grunt test」とやると、phantoms.js(とローカルサーバ)がまず起動し、その後テストが実行されている.

抜粋: grunt testした時の標準出力

Running "connect:test" (connect) task
Started connect web server on http://localhost:9001

Running "jasmine:all" (jasmine) task
Testing jasmine specs via PhantomJS

_SpecRunner.htmlをブラウザで開いたときは当然サーバーは動いていないので,XMLHttpRequestのsendを実行して失敗する.

テスト実行前に
sinon.stub(XMLHttpRequest.prototype)
を呼んでおくとsinon.jsがXMLHttpRequestに関する関数をラップして実行されなくするので_SpecRunner.htmlを開いてもエラーは出なくなる. ただし、fixtureが読み込まれないのでVIEWにテストが大幅に制約を受ける.

結局、裏側でサーバーを動かしてHeadlessでテストしろってことなんだと思う.

main.coffee

前回からの変更点としてbowwr_componentディレクトリがなぜかappディレクトリ下にも出来ていたのを削除. app/scripts/main.coffeeのrequire.configは2つ上のアプリケーションルートディレクトリを見るように変更.

app/scripts/main.cofee (関連部のみ抜粋)

require.config
  shim:
    bootstrap:
      deps: ['jquery'],
      exports: 'jquery'
  paths:
    jquery: '../../bower_components/jquery/dist/jquery'
    backbone: '../../bower_components/backbone/backbone'
    underscore: '../../bower_components/lodash/dist/lodash'
    bootstrap: '../../bower_components/sass-bootstrap/dist/js/bootstrap',

これでGruntfile.jsでパスを再指定する必要はなくなったが,今回sinon.jsを新たに利用するので,その分だけ付け足している.

Written with StackEdit.

1 comment:

  1. A lot of people continue to be not even determined that investing in wristwatches can be a shrewd thing to do and that is exactly primarily as a result of producers what individuals help to make economical looks after that do not necessarily performance the right way. Though here What i am saying is the most beneficial wristwatches with by far the most reliable web sites in the marketplace replica watches. His or her similarity to the traditional timepieces is unquestionable and that of which since they are made using the utmost attention to possibly even the littlest particulars. With your looks after a sensational scene to loss top quality for the money and also you find out that is not a frequent thing in the business! On-line to see purchaser critical reviews to influence oneself that not just your wrist watches happen to be fantastic replica watches uk, and also most of the customer! Usually any call appears to be orange, however this unique orange expression on the sapphire precious stone certainly is the anti-reflective coating on the gemstone, which often manufactures a fabulous bluish shade anytime seen on specific perspectives. So when the best website is actually generating his or her different watches further economical compared to they are http://www.attrinity.com, which can be the opportunity you never need to miss out. As well should you only discovered a different enjoy model and you need a vintage, Ebony Comes to an end will be best time to acquire the idea. And allow us to remember which usually Seasonal is normally getting and various other people in your own life would definitely delight in this type of present way too! For that reduced rate, I got all these important things as a result of your watch, to help you to visualize my best approval! Much like master watchmakers, backup designer watches companies likewise put in a great deal of effort along with carried out a continuous homework to better this primary charasteristics associated with watches. The main challenge can be to achieve the most beneficial effects with the help of lowest expenditures in order that most of these timepieces is often cost-effective for many people. Nevertheless they performed a great job plus this is one way natural families like my family is now able to own personal an ideal look at. The actual accurate of this Europe manufactured armor and weapon upgrades, the classiness belonging to the layout of this look at face not to mention fists, a persons vision to help you feature equity portion of the observe, they really are in excess of only just designer watches. They have been art works. Though that will not shows that I want to pay a great find to find considered one of my own. Fortuitously to me you will find excellent timepieces I can also shop for which use all the antique seem who I am going with regard to.

    ReplyDelete