測試是甚麼?
軟體測試是一種自動化的過程,它能對code進行斷言來判定結果,進而確保codebase的不崩壞。
測試可以粗略分成以下幾種
- 單元測試(unit test):測一個獨立的module、fuction、class的
邏輯
- 組件測試(component test)
- 整合測試(intergation test):測試module、fuction、class之間
交互作用
- 功能測試(functional test)
- 系統測試(system test)
- E2E test:模擬使用者行為去操作產品,測
畫面
測試前的準備工作
撰寫測試前需要先通用和抽象的要求淬煉成細節,然後寫成測試文件。
- 「管理員要能成功登入後台網站」
- 淬鍊成「管理員要可以在帳號、密碼input輸入正確的帳號、密碼,點下登入後成功登入」
- 寫成測試文件
測試的替身物件(test double)們
test double有5種-dummy、stub、fake、mock、spy。在測試時,SUT會是用真物件,而DOC通常會用假物件,這個假物件就是test double。
- SUT(System Under Test / 測試目標)
- DOC(Depended-on Component / 依賴元件)
dummy object
就只是個讓程式成功跑的假資料,不會對SUT造成影響。
export fakeData = [{name:'Bella', age:20, gender:'F'}, {name:'Jack', age:25, gender:'M'}, {name:'Vivian', age:50, gender:'F'}];
test stub(虛設常式)
寫死的假物件,方法的回傳值是寫死的,不會依測試時的情況做改變。著重在試狀態(state testing)
// 假設有一個真的function如下
declare type op = '=' | '>' | '<';
async function getData(tableName:string, condition:{left:string; op:op; right:string|number}[]):{name:string; age:number; gender:string}[]{
await fetch('https:.....',{
method:'GET',
header:{
Authorization: Authorization,
"X-Date": GMTString,
"Content-Type": "application/x-www-form-urlencoded"
},
body:JSON.stringfy({table:tableName, condition:condition}),
Accept-Encoding: 'gzip'
})).then((d) => {return d.data;})
}
// stub可能長這樣
const stub1 = jest.fn(() => {
return [{name:'Bella', age:20, gender:'F'}, {name:'Jack', age:25, gender:'M'}, {name:'Vivian', age:50, gender:'F'}];
});
// 或者這樣,之所以是空的是因為有時只是要確認有叫到function而已
const stub2 = jest.fn();
fake object
真物件的簡化版。裡面的資料都是寫死的,它只會模擬真物件的行為來回傳資料。
// 跟上一個例子一樣有一個getData function,它的mock可能長這樣
const fake = (tableName:string, condition:{left:string; op:op; right:string|number}[]) => {
if(condition[0].left === 'name'){
return fakeData.find((i:any) => i.name === 'Bella');
}
if(condition[0].left === 'age'){
return fakeData.find((i:any) => i.age === 20);
}
if(condition[0].left === 'gender'){
return fakeData.find((i:any) => i.gender === 'F');
}
}
mock object(模擬物件)
一個幾乎完全替代真物件的假物件。驗證方法預期被調用的情況(ex:呼叫function次數及返回值),著重在行為(behavioral testing)
// 跟上一個例子一樣有一個getData function,它的mock可能長這樣
const mock = (tableName:string, condition:{left:string; op:op; right:string|number}[]) => {
let res = fakeData;
condition.forEach(d => {
switch(d.op){
case '>':
res = res.filter((i:any) => i[d.left] > d.right);
break;
case '<':
res = res.filter((i:any) => i[d.left] < d.right);
break;
default:
res = res.filter((i:any) => i[d.left] === d.right);
break;
}
})
return res;
}
簡單說的話stub、fake、mock都是真物件的簡化版,差在模擬的精細度。
difference betweeen mock & stub
difference between faking, mocking and stubbing
TDD & ATDD & BDD
TDD(test driven development / 測試驅動開發)
從單元測試的角度出發(開發者的視角)。只有在測試失敗時才來改code,在敏捷開發的領域很受歡迎。- 開發者根據規格文件制定test case
- 跑測試
- 改沒通過測試的code
ATDD(acceptance test driven development / 驗收測試驅動開發)
從軟體需求的角度出發(著重在使用者的視角)。需要跨部門合作。- 先討論需求(真實操作時會發生的情況)
- 訂定驗收測試,
- 開始根據規格寫test case
- 跑測試
- 改沒通過測試的code
BDD(behavior driven development / 行為驅動開發)
衍生自BDD,TDD與ATDD的綜合體,從系統行為(Then)的角度出發。常用Given-When-Then格式來規劃test case。用(Given-When-Then格式)描述行為
Given the user has entered valid login credentials When a user clicks on the login button Then display the successful validation message
寫test case
- 跑測試
- 改沒通過測試的code
📖
什麼是SUT與DOC
Unit Test 中的替身
BDD vs TDD vs ATDD : Key Differences