Pages

搜尋此網誌

2014年10月22日 星期三

nodejs framework: How to testing sails and loopback

nodejs framework: How to testing sails and loopback

testing 真的很重要,每次我在跟別人說明時,都一再的強調,不過說實在的只有親身體驗過的才知道 testing 的好,再學一個語言或是框架時,我不再從 code style 下手,而是從如何 testing 開始。

enter image description here

如果你已經初步了解 testing 的方法,建議把寫 test 當作在寫 spec 這樣在撰寫時,不只是程式運作測試,最終目的是要有足夠的行為說明,以便暸解函式所要處理的問題,所以下面將會用 spec 取代 test 這個名詞。

之前有寫過的相關文章:

寫 spec 這件事情的好用文章也是很難敘述,不過大致上有下面幾點好處:

  • 快速驗證結果:任何後端 API 的改變可以很快的驗證,不需重覆啟動 > 開瀏覽器測試
  • 開發有節奏:不需離開鍵盤操作瀏覽器,屬於後端 API 的部分快速驗證
  • 前後端不用等:後端 API 確定好 input 與 output 後就可以前後端同步進行
  • 問題釐清容易:異常產生時,先確定 test 是否通過,在確認前端問題
  • 搭配 CI 確保程式品質
  • 透過 spec 的撰寫可以更快了解一個技術或是 framework 的特性
  • 規格清楚:不需要再猜測 API 的目的,透過 spec 的閱讀可以了解函式目的

太多好處了!不只是程式開發上方便,管理上也是,有機會再分享管理上的心得,鑑於萬事起頭難,下面將分享目前接觸的兩個框架的 spec 撰寫方式,方便有需要的人快速上手,也許你不一定使用我介紹的框架,但其實原理是一樣的,都可以參考看看,首先從 sails 開始,這邊不說明框架特性,那會是另外一個議題,讓我們從 spec 撰寫開始。

sails

官網在這,重點如下

測試資料

測試資料的定義可以在開始測試或是運行 server 時先有基本資料進行功能或是 sepc 的驗證,如此就不用每次開始都要在操作一次 CRUD 的流程,相關測試資料的建立可在 config/bootstrap.js 進行定義 下面用 coffee 撰寫如:

module.exports.bootstrap = (cb) ->
  User.findOrCreate
    username: "user"
  ,
    username: "user"
    email: "heywork01@gmail.com"
  , (error, user) ->
    cb(error)

如此再啟動 server 或是開始測試時,你就有個 user 可以使用,如此比如你接著要開發 login 的功能,就可以跳過 user 基本的 create 的操作直接進入登入功能的驗證。

測試前啟動 server

為了要進行測試我們希望可以實際啟動 server,如此我們才可以存取相關的單元進行測試,我們可以在 test 的 before 撰寫下列程式,同樣用 coffee:

Sails = require("sails")
describe "some spec", (done) ->
  before (done) ->

    Sails.lift {
      hooks:
        grunt:false
      log:
        level: "debug"
      models:
        connection: "testMysql"
        migrate: "drop"
    }, (error, sails) ->
      global.app = sails
      done error, sails

如此就會啟動 server,傳入的參數將複寫初始 config,方便微調不同的測試案例,接著我們就可以開始撰寫 spec。

撰寫 spec

因為 sails 的特性,所有 models 以及 service 都會在 server 啟動時被載入到記憶體,所以你可以很方便進行存取,下面假設已有建立 model: user 以及 service: UserService。

一般來說,開發流程上我們會先設計好 service,確認好單元功能沒問題後,再把相關 service 掛到 controller 上。

test 的進行會用到下面的 package,透過 mocha 進行:

should = require("should")
request = require("supertest")

service spec 撰寫

describe "user service spec", (done) ->
    it "create user", (done) ->

        params =
          name: "newUser"

        UserService.create params, (error , newUser) ->
          newUser.should.be.Object
          done()

rest api spec 撰寫

假設 UserService 的功能已經驗證完成,我們就可以將 service 在 controller 進行呼叫,因為 controller 只是進行 params 的傳遞,所以結構跟 params 很像,controller spec 的撰寫,可以讓前端開發者很清楚知道 rest url 還有預計取得的 response 的規格,不只當下清楚,新進人員加入時也同樣在那等著被讀取。

describe "user rest api spec", (done) ->
    it "do create", (done) ->

      request(sails.hooks.http.app).post("/user/create")
      .send(
        {name: 'newUser'}
      ).set("Content-Type", "application/json").end (error, res) ->
        res.statusCode.should.equal 200
        res.body.should.be.an.Object
        done()

掌握上面幾個關鍵點,就可以開始你的 spec 撰寫學習之旅,下面介紹的 loopback 雖然有些細節不同,但思維是一樣的,接著為大家說名。

loopback

最近在嘗試接觸的另外一個 nodejs framework,一些相關特性不在此篇範圍,在這還是 focus 在 spec 的撰寫。

loopback 跟現行的 MVC 的架構很不一樣,在查相關文件時,忘記在哪看到,他是 model oriented 的架構設計,所以在使用上一開始蠻不適應的,沒有像 sails 的 service 可以定義 business logic,所有程式的邏輯都在 model 裏,所以剛開始摸索他的 spec 以及實作時很卡,了解之後又是不一樣的風景,也總算可以好好欣賞一下。

以下就開始說明 loopback 測試方式,你會用到的 package 跟 sails 的方式相同,不一樣的是他有自己的 test package: loopback-testing,github 上的 readme 資訊非常的少,可以直接去看他的 test(spec),因為剛開始使用,所以沒辦法介紹的很完整,但至少可以開始進行 spec 撰寫與運行,同樣先從如何啟動 server 開始。因為文件敘述很少,不一定是最好的方式但至少目前是可行的方式。

啟動 server

require("../../server/server.coffee")
lt = require("loopback-testing")
describe "some test", (done) ->
    before (done) ->
      lt.beforeEach.withApp app
      done()

其中 server.coffee 的 code 如下:

require "coffee-script/register"
loopback = require("loopback")
boot = require("loopback-boot")
global.app = module.exports = loopback()

把 app 放在 global 是因為方便存取 model 進行類似 sails 的 service spec 驗證,lt.beforeEach.withApp app 這句就是啟動 server 了。

接著,假設我們定義好一個 model 如下:

module.exports = function(Plugin) {
  User.test = function(name, cb) {
    console.log("hello "+name);
    return cb(null, "hello "+name)
  }

  User.remoteMethod("test", {
    accepts: [
      {arg: "name", type: "string", required: true}
    ],
    returns: {arg: "result", type: "string"}
  });

開始撰寫測試,首先一樣先從單元功能開始。

model (service) spec 撰寫

  describe 'user spec', ->

    it "execute spec", (done) ->
      app.models.user.test "smlsun", (error, result) ->
        result.should.be.String
        result.should.be.equal "hello smlsun"
        done();

app.models.user.test 這樣就可以直接存取 model 的函式進行測試。

rest api spec 撰寫

可以用 sails rest api spec 的方式進行測試,也可以使用 loopback-testing package 進行,如下:

lt = require("loopback-testing")

describe "user rest api spec", (done) ->
    lt.describe.whenCalledRemotely "GET", "/api/users/test", {name:"smlsun"}, ->

      it "should have statusCode 200", ->
        assert.equal @res.statusCode, 200
        @res.body.should.have.property "result"
        @res.body.result.should.be.equal "hello smlsun"

就是這麼簡單!

結論

跟很多朋友聊過,通常介紹到 spec 撰寫的好處時,用言語實在很難讓聽者體會,希望透過這樣的介紹把萬事起頭難的不確定性說明得清楚一點,能幫助大家更快進入 spec 撰寫,BDD 或是 TDD 的開發思維裏,幫助寫出高品質的程式。

而在學習一個語言或是框架時,強烈建議從 spec 撰寫開始,如此一來不只是開發快速,關於語言特性也可以比較能夠掌握,spec 先行不只是個程式撰寫的技巧,他也是一種不重覆的哲學:「不重覆犯錯,不重覆浪費時間的事,不重覆在不重要的事」,用最有效率的方式達成你的目標,共勉之。

有任何問題歡迎詢問,spec 撰寫是個好東西,很喜歡跟大家分享,大家若有相同感受,別忘了告訴在你身邊一起奮戰的朋友,除了自己寫得開心,某方面來說,也算是促進台灣整體資訊產業品質,一起熱血吧!

張貼留言