Más contenido relacionado La actualidad más candente (20) Similar a 單元測試:Mocha、Chai 和 Sinon (18) 單元測試:Mocha、Chai 和 Sinon4. 什麼是 Unit Test?
以 function 為最小單位,驗證在特定情況下的 input 和 output 是否正確。
// 給定輸入為 1 和 2
var result = add(1, 2);
// 預期得到輸出 3
if (result !== 3) {
throw new Error('Failed');
}
5. 為什麼要做 Unit Test?
● 防止改 A 壞 B,避免不能跑的程式碼比能跑的還多
● 明確指出問題所在、告知正確的行為是什麼,減少猜測時間
無折扣碼、無運費優惠、無自填金額下,計算 total_amount_discount_h 的值。預期
是 999,結果是 990。
1) test Service: InvoiceCheckoutParamsService Method: getDiscountedFinalPrice no coupon, no discount delivery, no
custom price:
AssertionError: expected 990 to equal 999
+ expected - actual
-990
+999
at Context.<anonymous>
(/home/summer/samba/chongen22_20170904_PC_shopping_cart/www/include/resources/js/test/services/testInvoiceCheckoutPa
ramsService.js:98:25)
6. 如何做 Unit Test?
● 使用 npm 等安裝 Mocha、Chai 和 Sinon 並 require
● 撰寫測試程式
● 使用 mocha 啟動測試
● 檢視測試報告,確認通過或未通過的項目
● 根據測試結果進行調整:重構(通過)或除錯(未通過)
8. Mocha
● Mocha 是一個 JavaScript 的測試框架,目的是管理測試程式碼。
● BDD 提供 describe、it、before、after、beforeEach 與 afterEach 方法。
9. 語法
describe('hooks', function() { // 測試區塊
before(function() { /* 在所有測試開始前會執行的程式碼區塊 */ });
after(function() { /* 在所有測試結束後會執行的程式碼區塊 */ });
beforeEach(function() { /* 在每個 Test Case 開始前執行的程式碼區塊 */ });
afterEach(function() { /* 在每個 Test Case 結束後執行的程式碼區塊 */ });
// 撰寫個別 Test Case
it('should ...', function() { /* 執行 Test Case */ });
});
10. 比較 TDD 與 BDD
TDD BDD
全名 測試驅動開發
Test-Driven Development
行為驅動開發
Behavior Driven Development
定義 在開發前先撰寫測試程式,以確保程式
碼品質與符合驗收規格。
TDD 的進化版。
除了實作前先寫測試外,還要寫一份「可以執
行的規格」。
特性 ● 從測試去思考程式如何實作。
● 強調小步前進、快速且持續回饋、
擁抱變化、重視溝通、滿足需求。
● 從用戶的需求出發,強調系統行為。
● 使用自然語言描述測試案例,以減少使
用者和工程師的溝通成本。
● 測試後的輸出結果可以直接做為文件閱
讀。
12. Chai
Chai 提供 BDD 語法測試用的斷言庫(Assertion Library)。
斷言庫是一種判斷工具,驗證執行結果是否符合預期。
-> 若實際結果和預測不同,就是測到 bug 了。
13. 範例 1:Assert
assert(expression, message):測試這個項目的 expression 'foo' === 'bar' 是否為真,
若為假則顯示錯誤訊息 message。
var assert = chai.assert;
describe('AssertTest', function() {
var foo = 'Hello';
var bar = "World";
it('should be equal', function() {
assert('foo' === 'bar', 'foo is not bar');
});
});
14. 範例 2:Expect / Should
預期某個值會相等。例如:預期 3 等於(===)2。
var expect = chai.expect;
describe('ExpectTest', function() {
it('should be equal', function() {
expect(3).to.equal(2);
});
});
15. Assert, Expect, Should 的差異
三者基本上都可完成相同工作,除了
● Should 會修改 Object.prototype
● Should 在瀏覽器環境下,對 IE 有相容問題
● Should 無法客製化錯誤訊息
assert.isTrue(foo, "foo should be true");
expect(foo, "foo should be true").to.be.true;
19. Spy
function compare(former, latter, callback) {
callback();
return former === latter
}
describe('Spy', function() {
it('測試呼叫次數', function() {
var nameList = ['Nina', 'Ricky'];
var callback = sinon.spy();
compare(nameList[0], nameList[0], callback);
expect(callback.callCount).to.equal(999); // 該 function 被呼叫的次數
});
});
21. Stub 範例 1
const uuid = require('node-uuid');
describe('測試 uuid 的長度是否為 36', () => {
it('check length of uuid ', () => {
var stub = sinon.stub(uuid, 'v4');
var mockId = stub.v4();
expect(mockId.length).to.equal(32);
uuid.v4.restore();
});
});
22. Stub 範例 2:ajax
function saveUser(user, callback) {
$.ajax('/users', {
first: user.firstname,
last: user.lastname
}, callback);
}
23. Stub 範例 2:ajax
describe('Stub: ajax', () => {
it('should call callback after saving', () => {
var ajax = sinon.stub($, 'ajax'); // 取代 ajax function,並不會真的呼叫
ajax.yields('Hello', 'World'); // 準備把傳入 callback 的參數 ['Hello', 'World']
丟進去
var callback = sinon.spy();
saveUser({ firstname: 'Han', lastname: 'Solo' }, callback);
ajax.restore(); // 清除 test double
expect(callback.callCount).to.equal(777); // 該 function 被呼叫的次數
});
});
26. Mock
describe('Mock', () => {
it('should pass Hello World to run call()', function() {
var mock = sinon.mock(opts);
mock.expects('call').once().withExactArgs('Hello World');
opts.call('Hello World');
mock.restore();
mock.verify();
});
});
27. Chai Assertion vs Sinon Assertion
推薦使用 Sinon 的 Assertion,因為
● 不需刻意客製化報錯,即可反應問題所在
assert('foo' === 'bar'); // AssertionError: Unspecified AssertionError
assert('foo' === 'bar', 'foo is not bar'); // AssertionError: foo is not
bar
var expected = {x: 999}, actual = {x: 1, y: 2};
sinon.assert.match(actual, expected);
/* AssertError: expected value to match
expected = { x: 999 }
actual = { x: 1, y: 2 } */
30. 練習 1
function isGod() {
return true
}
describe('猜猜看會不會出錯', function() {
it('expect 練習', function() {
const OK = 999;
expect(isGod()).to.equal(OK);
});
});