SlideShare una empresa de Scribd logo
1 de 31
Descargar para leer sin conexión
單元測試:Mocha、Chai 和 Sinon
Agenda
● Unit Test
● Mocha
● Chai
● Sinon
● 露天桌機結帳範例
● QnA
● 練習 1, 2
單元測試(Unit Test)
什麼是 Unit Test?
以 function 為最小單位,驗證在特定情況下的 input 和 output 是否正確。
// 給定輸入為 1 和 2
var result = add(1, 2);
// 預期得到輸出 3
if (result !== 3) {
throw new Error('Failed');
}
為什麼要做 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)
如何做 Unit Test?
● 使用 npm 等安裝 Mocha、Chai 和 Sinon 並 require
● 撰寫測試程式
● 使用 mocha 啟動測試
● 檢視測試報告,確認通過或未通過的項目
● 根據測試結果進行調整:重構(通過)或除錯(未通過)
Mocha
Mocha
● Mocha 是一個 JavaScript 的測試框架,目的是管理測試程式碼。
● BDD 提供 describe、it、before、after、beforeEach 與 afterEach 方法。
語法
describe('hooks', function() { // 測試區塊
before(function() { /* 在所有測試開始前會執行的程式碼區塊 */ });
after(function() { /* 在所有測試結束後會執行的程式碼區塊 */ });
beforeEach(function() { /* 在每個 Test Case 開始前執行的程式碼區塊 */ });
afterEach(function() { /* 在每個 Test Case 結束後執行的程式碼區塊 */ });
// 撰寫個別 Test Case
it('should ...', function() { /* 執行 Test Case */ });
});
比較 TDD 與 BDD
TDD BDD
全名 測試驅動開發
Test-Driven Development
行為驅動開發
Behavior Driven Development
定義 在開發前先撰寫測試程式,以確保程式
碼品質與符合驗收規格。
TDD 的進化版。
除了實作前先寫測試外,還要寫一份「可以執
行的規格」。
特性 ● 從測試去思考程式如何實作。
● 強調小步前進、快速且持續回饋、
擁抱變化、重視溝通、滿足需求。
● 從用戶的需求出發,強調系統行為。
● 使用自然語言描述測試案例,以減少使
用者和工程師的溝通成本。
● 測試後的輸出結果可以直接做為文件閱
讀。
Chai
Chai
Chai 提供 BDD 語法測試用的斷言庫(Assertion Library)。
斷言庫是一種判斷工具,驗證執行結果是否符合預期。
-> 若實際結果和預測不同,就是測到 bug 了。
範例 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');
});
});
範例 2:Expect / Should
預期某個值會相等。例如:預期 3 等於(===)2。
var expect = chai.expect;
describe('ExpectTest', function() {
it('should be equal', function() {
expect(3).to.equal(2);
});
});
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;
Sinon
Sinon
用來產生 Test Double(測試替身),簡單來說就是假資料,分為 3 種
● Spy
● Stub
● Mock
Spy
對 function call 蒐集資訊,便於對測試結果做驗證。
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 被呼叫的次數
});
});
Stub
● 取代 function。與 Spy 不同的是,Spy 依然會執行真的 function,但 Stub 並不
會。
● 適用以下狀況
○ Ajax
○ Timer
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();
});
});
Stub 範例 2:ajax
function saveUser(user, callback) {
$.ajax('/users', {
first: user.firstname,
last: user.lastname
}, callback);
}
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 被呼叫的次數
});
});
Mock
取代整個物件,包含完整實作細節。
Mock
var opts = {
call: function (msg) {
console.log(msg);
}
};
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();
});
});
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 } */
露天桌機結帳範例
InvoiceCheckoutParamsService.js
● 取得 discount id
● 取得 coupon id
● 各類情況下,是否正確計算 total_amount_discount_h 的值
● 各類情況下,是否正確計算 total_amount_h 的值
QnA
● Q:寫測試是否會增加額外工時?
A:工時是一定會增加的,個人經驗是增加一倍。
● Q:除了程式碼的品質保證外,還有什麼好處?
A:記錄規格。測試案例如同告知開發者規格的細節和範例,再也不怕同事離職,
無人可問。
練習 1
function isGod() {
return true
}
describe('猜猜看會不會出錯', function() {
it('expect 練習', function() {
const OK = 999;
expect(isGod()).to.equal(OK);
});
});
練習 2
describe('猜猜看會不會出錯', function() {
it('練習 2', function() {
assert(null == undefined, 'null 不等於 undefined');
});
})
// 獎品是一根大香蕉

Más contenido relacionado

La actualidad más candente

Cancer du sein 2010, mg
Cancer du sein 2010, mgCancer du sein 2010, mg
Cancer du sein 2010, mg
esf3
 
Analysis of clinical trials using sas 勉強用 isseing333
Analysis of clinical trials using sas 勉強用 isseing333Analysis of clinical trials using sas 勉強用 isseing333
Analysis of clinical trials using sas 勉強用 isseing333
Issei Kurahashi
 

La actualidad más candente (20)

マルコフ連鎖モンテカルロ法と多重代入法
マルコフ連鎖モンテカルロ法と多重代入法マルコフ連鎖モンテカルロ法と多重代入法
マルコフ連鎖モンテカルロ法と多重代入法
 
《論語八則》.pptx
《論語八則》.pptx《論語八則》.pptx
《論語八則》.pptx
 
金融情報における時系列分析
金融情報における時系列分析金融情報における時系列分析
金融情報における時系列分析
 
有限要素法見本
有限要素法見本有限要素法見本
有限要素法見本
 
怎樣才算是一個合格的資工系畢業生
怎樣才算是一個合格的資工系畢業生怎樣才算是一個合格的資工系畢業生
怎樣才算是一個合格的資工系畢業生
 
系統程式 (習題:Nand2tetris軟體部分)
系統程式 (習題:Nand2tetris軟體部分)系統程式 (習題:Nand2tetris軟體部分)
系統程式 (習題:Nand2tetris軟體部分)
 
臨床医からみた医学統計
臨床医からみた医学統計臨床医からみた医学統計
臨床医からみた医学統計
 
熱傷性ショックに対する輸液とモニタリング
熱傷性ショックに対する輸液とモニタリング熱傷性ショックに対する輸液とモニタリング
熱傷性ショックに対する輸液とモニタリング
 
寫給資工系畢業生的工作面試指南
寫給資工系畢業生的工作面試指南寫給資工系畢業生的工作面試指南
寫給資工系畢業生的工作面試指南
 
第5回 「痙攣,てんかん」
第5回 「痙攣,てんかん」第5回 「痙攣,てんかん」
第5回 「痙攣,てんかん」
 
用十分鐘學會 《微積分、工程數學》及其應用
用十分鐘學會  《微積分、工程數學》及其應用用十分鐘學會  《微積分、工程數學》及其應用
用十分鐘學會 《微積分、工程數學》及其應用
 
熱傷についての基礎知識
熱傷についての基礎知識熱傷についての基礎知識
熱傷についての基礎知識
 
関係フレーム理論
関係フレーム理論関係フレーム理論
関係フレーム理論
 
Cancer du sein 2010, mg
Cancer du sein 2010, mgCancer du sein 2010, mg
Cancer du sein 2010, mg
 
適合資工系畢業生的 一百零一種工作
適合資工系畢業生的  一百零一種工作適合資工系畢業生的  一百零一種工作
適合資工系畢業生的 一百零一種工作
 
第8回 「熱傷」
第8回 「熱傷」第8回 「熱傷」
第8回 「熱傷」
 
Analysis of clinical trials using sas 勉強用 isseing333
Analysis of clinical trials using sas 勉強用 isseing333Analysis of clinical trials using sas 勉強用 isseing333
Analysis of clinical trials using sas 勉強用 isseing333
 
開放電腦計畫 - 從一顆最簡單的 MCU 談起
開放電腦計畫  - 從一顆最簡單的 MCU 談起開放電腦計畫  - 從一顆最簡單的 MCU 談起
開放電腦計畫 - 從一顆最簡單的 MCU 談起
 
用十分鐘瞭解《線性代數、向量微積分》以及電磁學理論
用十分鐘瞭解《線性代數、向量微積分》以及電磁學理論用十分鐘瞭解《線性代數、向量微積分》以及電磁學理論
用十分鐘瞭解《線性代數、向量微積分》以及電磁學理論
 
1 6.変数選択とAIC
1 6.変数選択とAIC1 6.変数選択とAIC
1 6.変数選択とAIC
 

Similar a 單元測試:Mocha、Chai 和 Sinon

GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结
coderzh
 
Net Parallel Programming .NET平行處理與執行序
Net Parallel Programming .NET平行處理與執行序Net Parallel Programming .NET平行處理與執行序
Net Parallel Programming .NET平行處理與執行序
HO-HSUN LIN
 
Java script测试之js unit ut
Java script测试之js unit utJava script测试之js unit ut
Java script测试之js unit ut
fangdeng
 
C语言学习100例实例程序
C语言学习100例实例程序C语言学习100例实例程序
C语言学习100例实例程序
yiditushe
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型
keelii
 
神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)
yiditushe
 
第六章 函數與巨集
第六章 函數與巨集第六章 函數與巨集
第六章 函數與巨集
shademoon
 
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutorInside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Ady Liu
 
2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發
AgileCommunity
 

Similar a 單元測試:Mocha、Chai 和 Sinon (18)

GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结
 
前端测试
前端测试前端测试
前端测试
 
前端测试
前端测试前端测试
前端测试
 
Net Parallel Programming .NET平行處理與執行序
Net Parallel Programming .NET平行處理與執行序Net Parallel Programming .NET平行處理與執行序
Net Parallel Programming .NET平行處理與執行序
 
Sql培训 (1)
Sql培训 (1)Sql培训 (1)
Sql培训 (1)
 
Java script测试之js unit ut
Java script测试之js unit utJava script测试之js unit ut
Java script测试之js unit ut
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程
 
C语言学习100例实例程序
C语言学习100例实例程序C语言学习100例实例程序
C语言学习100例实例程序
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型
 
Python 迴圈作業
Python 迴圈作業Python 迴圈作業
Python 迴圈作業
 
神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)神州泰岳测试试题(笔试)
神州泰岳测试试题(笔试)
 
JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能JCConf 2023 - 深入淺出 Java 21 功能
JCConf 2023 - 深入淺出 Java 21 功能
 
JavaScript 80+ Programming and Optimization Skills
JavaScript 80+ Programming and Optimization SkillsJavaScript 80+ Programming and Optimization Skills
JavaScript 80+ Programming and Optimization Skills
 
第六章 函數與巨集
第六章 函數與巨集第六章 函數與巨集
第六章 函數與巨集
 
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutorInside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
 
2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發
 
Os讀書會20170609
Os讀書會20170609Os讀書會20170609
Os讀書會20170609
 
Maintainable Javascript
Maintainable JavascriptMaintainable Javascript
Maintainable Javascript
 

單元測試:Mocha、Chai 和 Sinon

  • 2. Agenda ● Unit Test ● Mocha ● Chai ● Sinon ● 露天桌機結帳範例 ● QnA ● 練習 1, 2
  • 4. 什麼是 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 的進化版。 除了實作前先寫測試外,還要寫一份「可以執 行的規格」。 特性 ● 從測試去思考程式如何實作。 ● 強調小步前進、快速且持續回饋、 擁抱變化、重視溝通、滿足需求。 ● 從用戶的需求出發,強調系統行為。 ● 使用自然語言描述測試案例,以減少使 用者和工程師的溝通成本。 ● 測試後的輸出結果可以直接做為文件閱 讀。
  • 11. Chai
  • 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;
  • 16. Sinon
  • 18. Spy 對 function call 蒐集資訊,便於對測試結果做驗證。
  • 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 被呼叫的次數 }); });
  • 20. Stub ● 取代 function。與 Spy 不同的是,Spy 依然會執行真的 function,但 Stub 並不 會。 ● 適用以下狀況 ○ Ajax ○ Timer
  • 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 被呼叫的次數 }); });
  • 25. Mock var opts = { call: function (msg) { console.log(msg); } };
  • 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 } */
  • 28. 露天桌機結帳範例 InvoiceCheckoutParamsService.js ● 取得 discount id ● 取得 coupon id ● 各類情況下,是否正確計算 total_amount_discount_h 的值 ● 各類情況下,是否正確計算 total_amount_h 的值
  • 30. 練習 1 function isGod() { return true } describe('猜猜看會不會出錯', function() { it('expect 練習', function() { const OK = 999; expect(isGod()).to.equal(OK); }); });
  • 31. 練習 2 describe('猜猜看會不會出錯', function() { it('練習 2', function() { assert(null == undefined, 'null 不等於 undefined'); }); }) // 獎品是一根大香蕉