Node.js單元測試&CI CD
講者介紹
- 李昀陞
- Peter
- 後端開發者
- 技術宅
- email: peter279k@gmail.com
在開始講單元測試之前
什麼是JavaScript?
JavaScript
- JavaScript != JAVA
- 一個瀏覽器上運行程式語言
- 在網頁上活躍
- 以ECMAScript實現語言標準化
那什麼是Node.js
Node.js
- 不是程式語言
- 是一個環境讓JavaScript可以運行在後端
- 基於Google開發V8引擎
- 跨平台
Node.js特色
- 非阻塞式(non-blocking)
- 事件驅動(Event-driven)
Node.js環境安裝
Windows
- 下載連結
- 目前Release版本:9.2.0
- LTS版本:8.9.2
相依套件管理
NPM(Node Package Manager)
YARN
套件市集
- 可以瀏覽與搜尋套件
- 發佈套件
- https://www.npmjs.com/
進入正題吧!
什麼是單元測試?
在講單元測試之前…
談談測試的類型
測試類型
單元測試
- 單位/單元=function or method
- 最小單位功能
- xUnit測試框架
Node.js上的單位測試
Lab 1&Lab 2
Assertions
建置Lab 1
- 下載Git-For-Windows
- 打開Git-bash
- git clone https://github.com/peter279k/Calculator.js
-
打開Node.js command prompt
- cd Calcualtor.js
- npm install
src/util/Calculator.js
function Calculator(num1, num2) {
this.num1 = num1;
this.num2 = num2;
this.add = function(num1 = this.num1, num2 = this.num2) {
if(isNaN(num1)) {
throw new TypeError(num1 + ' is not the valid number');
}
if(isNaN(num2)) {
throw new TypeError(num2 + ' is not the valid number');
}
return parseFloat(num1) + parseFloat(num2);
};
this.minus = function(num1 = this.num1, num2 = this.num2) {
if(isNaN(num1)) {
throw new TypeError(num1 + ' is not the valid number');
}
if(isNaN(num2)) {
throw new TypeError(num2 + ' is not the valid number');
}
return parseFloat(num1) - parseFloat(num2);
}
this.mul = function(num1 = this.num1, num2 = this.num2) {
if(isNaN(num1)) {
throw new TypeError(num1 + ' is not the valid number');
}
if(isNaN(num2)) {
throw new TypeError(num2 + ' is not the valid number');
}
return parseFloat(num1) * parseFloat(num2);
}
this.divide = function(num1 = this.num1, num2 = this.num2) {
if(isNaN(num1)) {
throw new TypeError(num1 + ' is not the valid number');
}
if(isNaN(num2)) {
throw new TypeError(num2 + ' is not the valid number');
}
num1 = parseFloat(num1);
num2 = parseFloat(num2);
if(num2 == 0) {
throw new Error(num2 + ' should not be the zero number');
}
return num1 / num2;
}
}
module.exports = Calculator;
src/util/SimpleMath.js
function SimpleMath(num1, num2) {
this.num1 = num1;
this.num2 = num2;
this.pow = function(num1 = this.num1, num2 = this.num2) {
if(isNaN(num1)) {
throw new TypeError(num1 + ' is not the valid number');
}
if(isNaN(num2)) {
throw new TypeError(num2 + ' is not the valid number');
}
num1 = parseFloat(num1);
num2 = parseFloat(num2);
return Math.pow(num1, num2);
}
this.sqrt = function(num = this.num) {
if(isNaN(num)) {
throw new TypeError(num + ' is not the valid number');
}
num = parseFloat(num);
return Math.sqrt(num);
}
this.log = function(num = this.num1) {
if(isNaN(num)) {
throw new TypeError(num + ' is not the valid number');
}
num = parseFloat(num);
return Math.log(num);
}
this.logTen = function(num = this.num1) {
if(isNaN(num)) {
throw new TypeError(num + ' is not the valid number');
}
num = parseFloat(num);
return Math.log10(num);
}
}
module.exports = SimpleMath;
test/CalculatorTest.js
const assert = require('assert');
var calculatorjs = require('../src/main');
var Calculator = calculatorjs.Calculator;
var SimpleMath = calculatorjs.SimpleMath;
var calculator = new Calculator(1, 2);
var simpleMath = new SimpleMath(2, 3);
// assert.equal(expected, result);
assert.equal(3, calculator.add());
assert.equal(-1, calculator.minus());
assert.equal(2, calculator.mul());
assert.equal(0.5, calculator.divide());
assert.equal(8, simpleMath.pow());
assert.equal(2, simpleMath.sqrt(4));
assert.equal(0, simpleMath.log(1));
assert.equal(0, simpleMath.logTen(1));
// It's equivalent to assert.throws(function() {calculator.add('ff', 'ff');}, TypeError);
assert.throws(() => calculator.add('ff', 'ff'), TypeError);
assert.throws(() => calculator.minus('ff', 'ff'), TypeError);
assert.throws(() => calculator.mul('ff', 'ff'), TypeError);
assert.throws(() => calculator.divide('ff', 'ff'), TypeError);
assert.throws(() => calculator.divide(1, 0), Error);
npm run assert-test
Mocha
- 補足內建Assert的不足
- 實踐TDD(Test-Driven Development)
- 實踐BDD(Behavior-Driven Development)
npm test
紅燈
綠燈
程式碼涵蓋率
- Code Coverage
- 確認程式碼是否有執行到
- 努力到100%
程式碼涵蓋率工具
- istanbul
- npm run istanbul-test
- npm run mocha-istanbul-text-test
- npm run mocha-istanbul-html-test
npm run istanbul-test
npm run mocha-istanbul-text-test
npm run mocha-istanbul-html-test
./coverage/index.html
涵蓋率測試方法
- Graph Coverage
- Stress Testing Coverage
- Logical Expression Coverage
Graph Coverage
- Edge Coverage
- Edge-Pair Coverage
- Test Path Coverage
Stress Testing Coverage
- Unexpected Test Case
- Empty String
- Big Number
- Strings
Logical Expression Coverage
- Predicate Coverage
- Clause Coverage
- Combinatorial Coverage
Continuous Integration
- 持續整合
- 每當專案有commit時啟動
- 可以自動化做測試
Travis-CI
- 線上免費的CI
- https://travis-ci.org/
串接範例(.travis.yml)
sudo: false
language: node_js
node_js:
- 6
- 8
- "stable"
script: npm test
after_success:
- npm run-script coveralls-coverage
- npm run-script codecov-coverage
將專案與Travis-CI連接
Code Coverage Service連接
Code Coverage Service連接
Lab 2內容
- Coordinate.js
- 判斷座標在第幾個象限
Lab 2 Live Demo
涵蓋率方法實踐
Any Questions?
Thank you for your listening!
Nodejs的單元測試&CI CD
By peter279k
Nodejs的單元測試&CI CD
- 1,643