各位是否還記得在 Day 12 - Infrastructure - Isomorphic API 中,我們提出了 API 特殊寫法與用法,事實上這樣的寫法除了滿足 Isomorphism 之外,它還是一種可測試(Testable)的寫法,請見本文說明。
從流程的觀點來看,我們要先描述目前要測試哪一個 API,還有要測試該 API 的哪些 Methods,例如我想測試 todoAPI 的 create() 和 list():
describe('#todoAPI', () => { describe('#create()', () => { // ... });
describe('#list()', () => { // ... });});
在測試過程中我們會動到測試資料庫,所以開始測試的前後,我慣例上會先清空所有資料:
import Todo from '../../../build/server/models/Todo';
describe('#todoAPI', () => { before((done) => { Todo.remove({}, done); });
describe('#create()', () => { // ... });
describe('#list()', () => { // ... });
after((done) => { Todo.remove({}, done); });});
然後再 Import 待測試的 API,準備好假資料,照著平常呼叫 API 的寫法來使用即可:
import { apiEngine } from '../../utils';import todoAPI from '../../../build/common/api/todo';import async from 'async';import Todo from '../../../build/server/models/Todo';
describe('#todoAPI', () => { let fakeTodos = [{ text: 'this is a fake todo text', }, { text: 'foo', }, { text: '~bar~', }];
before((done) => { Todo.remove({}, done); });
describe('#create()', () => { it('should create todo', (done) => { async.eachSeries(fakeTodos, (fakeTodo, cb) => { todoAPI(apiEngine) .create(fakeTodo) .then((json) => { // ... cb(); }); }, done); }); });
describe('#list()', () => { it('should list todos', (done) => { todoAPI(apiEngine) .list({ page: 1 }) .then((json) => { // ... done(); }); }); });
after((done) => { Todo.remove({}, done); });});
由於測試資料可能會有很多筆,所以這裡還使用到了 open_in_new 這套 Library,輔助我們在非同步的環境下按照順序走過全部的測資。
API 本身的錯誤已經被包裝在 Promise 的 Catch 裡了,所以我們故意不處理 Catch,萬一真的發生錯誤,Mocha 會因為 Callback Function 沒有被呼叫而噴出 Timeout 錯誤,使該測試失敗。
至於如何驗證 Response 的資料一切正常,我們用到的是 這套 Assertion Library:
import chai from 'chai';import { apiEngine } from '../../utils';import todoAPI from '../../../build/common/api/todo';import async from 'async';import Todo from '../../../build/server/models/Todo';let expect = chai.expect;
describe('#todoAPI', () => { let fakeTodos = [{ text: 'this is a fake todo text', }, { text: 'foo', }, { text: '~bar~', }];
before((done) => { Todo.remove({}, done); });
describe('#create()', () => { it('should create todo', (done) => { async.eachSeries(fakeTodos, (fakeTodo, cb) => { todoAPI(apiEngine) .create(fakeTodo) .then((json) => { expect(json.todo).to.be.an('object'); expect(json.todo.text).to.equal(fakeTodo.text); cb(); }); }, done); }); });
describe('#list()', () => { it('should list todos', (done) => { todoAPI(apiEngine) .list({ page: 1 }) .then((json) => { expect(json.todos).to.be.an('array'); expect(json.todos).to.have.lengthOf(fakeTodos.length); done(); }); }); });
after((done) => { Todo.remove({}, done); });});
Chai 有很多語意化的 Chained Method 可以使用,讓我們可以很容易看懂測試的項目,例如上方程式中的 ,可以很直覺地知道這項測試的目的是:預期 todoAPI.create() 的回傳值中的 todo.text 要等於我們 Post 過去的 fakeTodo.text,其他的測試亦同理,Chai 還有很多奇奇怪怪的語法可以使用,建議讀者們遇到相對應的 Testing Scenario 再去查詢即可。
Day 22 - Testing - 撰寫 End-To-End API 測試
Day 22 - Testing - 撰寫 End-To-End API 測試