像蓋房子同樣寫代碼:當我以測試驅動開發的時候,我在想些什麼

當我寫一個功能模塊方法時,我在想些什麼

// 不管什麼方法,都是這樣一個結構
const fn = () => {

};

好比,我要寫一個接口,查詢組織下的設備列表 /api/device/listapi

地基

const deviceList = (params) => { // 傳入一些參數
  return []; // 返回一個列表
};

我須要哪些參數:數組

  • 用戶基本信息(主要是用戶 id,用戶的組織 id)
  • 用戶對應的組織基本信息(主要是組織 id,組織管理員 id,層級關係,以及權限邏輯)

輸出結果很簡單,爲一個數組。session

澆築

第一步分析,存在成功和錯誤(錯誤類型先不考慮)兩種類型的結果。框架

// 成功
// 錯誤
const deviceList = async (ctx) => {
  // 錯誤
  if(someError) {
    // 返回錯誤結果
  }
  // 成功
  return getDevicesByOid(oid);
};

這是一個大概的設想,沒有必要將代碼寫出來。而後潤化該思路,寫出第一段框架。async

主體結構

首先,傳入的參數爲組織 oid,用戶的信息能夠經過 session(或其餘方式)從內部得到。單元測試

可能的一種思路

// 成功
// 錯誤
// 錯誤1:用戶未加入組織
// 錯誤2:傳入參數組織不存在
// 錯誤3:用戶無組織權限

// 傳入參數: 要查詢的組織 oid
// 可以經過 session 取到的信息: user
const deviceList = async (ctx) => {
  // 用戶信息 ctx.user
  // 判斷用戶是否有組織
  if (ctx.user.oid === 0) {
    // 錯誤1:用戶未加入組織
  }

  // 若是不傳該參數,查詢當前用戶組織的設備
  const { oid = ctx.user.oid } = ctx.request.body;
  if (oid === ctx.user.oid) {
    // 成功
    return getDevicesByOid(oid);
  }

  // 根據oid查詢組織信息
  // 錯誤2:傳入參數組織不存在
  // 判斷是否有權限
  const checkRights = await checkUserOrgRights(ctx.user.uid, oid);
  if (!checkRights) {
    // 錯誤3:用戶無組織權限
  }
  // 成功
  return getDevicesByOid(oid);
};

推薦的實現方式

// 成功
// 錯誤
// 錯誤1:用戶未加入組織
// 錯誤2:傳入參數組織不存在
// 錯誤3:用戶無組織權限

// 傳入參數: 要查詢的組織 oid
// 可以經過 session 取到的信息: user
const deviceList = async (ctx) => {
  // 用戶信息 ctx.user
  // 判斷用戶是否有組織
  if (ctx.user.oid === 0) {
    // 錯誤1:用戶未加入組織
  }

  // 若是不傳該參數,查詢當前用戶組織的設備
  const { oid = ctx.user.oid } = ctx.request.body;
  if (oid !== ctx.user.oid) {
    // 爲何這裏不用等於判斷:若是等於的話,則當時就須要返回出去,這樣的話該方法會有兩個成功的 return
    // 根據oid查詢組織信息
    // 錯誤2:傳入參數組織不存在
    // 判斷是否有權限
    const checkRights = await checkUserOrgRights(ctx.user.uid, oid);
    if (!checkRights) {
      // 錯誤3:用戶無組織權限
    }
  }
  // 成功
  return getDevicesByOid(oid);
};

封頂

完成其餘的業務代碼。測試

當我寫一段測試的時候,我在想些什麼

按照上面推薦方式完成代碼後,須要進行代碼的測試。優化

首先須要明確業務的流程,理清測試的思路。ui

  • 成功
  • 錯誤設計

    • 錯誤1:用戶未加入組織
    • 錯誤2:傳入參數組織不存在
    • 錯誤3:用戶無組織權限

主要有兩種設計思路:

設計思路

思路一

  1. 完成測試用例,覆蓋成功的全部狀況
  2. 完成測試用例,覆蓋錯誤1的全部狀況
  3. 完成測試用例,覆蓋錯誤2的全部狀況
  4. 完成測試用例,覆蓋錯誤3的全部狀況

這是傳統的單元測試衍生而來的 BDD 測試方式。

這裏測試用例的個數應該爲8次:

  • 成功:

    • 1.當前組織的用戶有傳入組織 oid
    • 2.當前組織的用戶未傳入組織 oid
    • 3-5.上級組織,上上級組織,根級組織的管理員用戶傳入組織 oid
  • 6.失敗1:用戶未加入組織
  • 7.失敗2:傳入參數組織不存在
  • 8.失敗3:用戶無組織權限

其中,測試3-5能夠優化爲一次測試(即根據全部管理員 uid 的數組比較是否包含當前用戶 uid),最終優化後的結果應當爲6次。

但因爲該思路中不明確用戶,因此用戶行爲沒法準確表達,在建立測試數據的時候較爲困難,不仔細思考分析,沒法優化須要建立多少條測試數據。

思路二

而實際上 BDD 測試爲用戶行爲測試,能夠以幾類用戶的情形分別進行測試。

  1. 模擬一個用戶的數據,覆蓋成功和可能錯誤(有可能沒法涵蓋到全部錯誤)的全部狀況
  2. 根據未覆蓋的部分,再模擬另外一個用戶的數據,覆蓋成功和可能錯誤(有可能沒法涵蓋到全部錯誤)的全部狀況

以此循環,直至覆蓋全部。

  • 用戶1(非組織管理員,查詢本身的組織)

    • 1.成功(未傳入組織 oid)(組織1)
    • 2.成功(傳入組織 oid)
    • 3.失敗2:傳入參數組織不存在
    • 4.失敗3:用戶無組織權限(組織2)
  • 用戶2(上級某組織管理員)(組織3)

    • 5.成功
  • 用戶3(未加入組織用戶)

    • 6.失敗1:用戶未加入組織

很是簡潔明瞭的關係,須要3個測試用戶,3個組織(上下級關係進行數據複用,一個無權限的組織),便可涵蓋全部範圍。

最終優化版設計:

  • 用戶1(某組織管理員,有下級組織)

    • 1.成功(未傳入組織 oid,查詢本身的組織)
    • 2.成功(傳入當前的組織 oid(組織1))
    • 3.成功(傳入下級的組織 oid(組織2))
    • 4.失敗2:傳入參數組織不存在
    • 5.失敗3:用戶無組織權限
  • 用戶2(未加入組織用戶)

    • 6.失敗1:用戶未加入組織(組織3)

兩個用戶,三個組織。完成全部覆蓋。

當我以測試驅動開發的時候,我在想些什麼

能夠從上述測試思路二中進行反推。

實際上思路多是在寫代碼或者寫測試的過程當中不斷的改進和完善的。

  • 若是已經寫好了測試正在寫代碼,能夠及時回過頭來調整測試;
  • 若是功能寫好了又再從新測試,能夠在測試優化後再去看邏輯代碼是否還有優化的空間。

更多關注: https://leader.js.cool/#/expe...

相關文章
相關標籤/搜索