測試


Posted by TempuraEngineer on 2022-03-12

測試是甚麼?

軟體測試是一種自動化的過程,它能對code進行斷言來判定結果,進而確保codebase的不崩壞。

測試可以粗略分成以下幾種

  1. 單元測試(unit test):測一個獨立的module、fuction、class的邏輯
  2. 組件測試(component test)
  3. 整合測試(intergation test):測試module、fuction、class之間交互作用
  4. 功能測試(functional test)
  5. 系統測試(system test)
  6. E2E test:模擬使用者行為去操作產品,測畫面

測試前的準備工作

撰寫測試前需要先通用和抽象的要求淬煉成細節,然後寫成測試文件。

  1. 「管理員要能成功登入後台網站」
  2. 淬鍊成「管理員要可以在帳號、密碼input輸入正確的帳號、密碼,點下登入後成功登入」
  3. 寫成測試文件


測試的替身物件(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

mocks vs stubs

difference between faking, mocking and stubbing


TDD & ATDD & BDD

  • TDD(test driven development / 測試驅動開發)
    從單元測試的角度出發(開發者的視角)。只有在測試失敗時才來改code,在敏捷開發的領域很受歡迎。

    1. 開發者根據規格文件制定test case
    2. 跑測試
    3. 改沒通過測試的code
  • ATDD(acceptance test driven development / 驗收測試驅動開發)
    從軟體需求的角度出發(著重在使用者的視角)。需要跨部門合作。

    1. 先討論需求(真實操作時會發生的情況)
    2. 訂定驗收測試,
    3. 開始根據規格寫test case
    4. 跑測試
    5. 改沒通過測試的code
  • BDD(behavior driven development / 行為驅動開發)
    衍生自BDD,TDD與ATDD的綜合體,從系統行為(Then)的角度出發。常用Given-When-Then格式來規劃test case。

    1. 用(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
      
    2. 寫test case

    3. 跑測試
    4. 改沒通過測試的code


📖

什麼是SUT與DOC
Unit Test 中的替身
BDD vs TDD vs ATDD : Key Differences


#測試 #mock #stub







Related Posts

C 語言練習程式(7) -- 直接改變陣列內容&利用指標達成回傳型態轉換 -- 指標相關程式集錦(6)

C 語言練習程式(7) -- 直接改變陣列內容&利用指標達成回傳型態轉換 -- 指標相關程式集錦(6)

[MSSQL] 取得特定年、月、星期的所有日期

[MSSQL] 取得特定年、月、星期的所有日期

原型繼承與原型鏈(待補筆記)

原型繼承與原型鏈(待補筆記)


Comments