聊聊前端測試

yhlben發表於2019-07-14

長期以來,前端測試並不是開發者必須要編寫的,但隨著前端工程化的發展,前端測試已經成為持續整合中重要的組成部分,編寫前端測試可以達到以下目的:

  • 正確性:可以驗證程式碼的正確性,在上線前心裡有底
  • 自動化:通過編寫測試用例,可以做到一次編寫,多次執行
  • 解釋性:測試用例中會設計如何使用這些 API,良好的測試用例比開發文件更直觀
  • 驅動開發:要保證程式碼的可測試性,就需要在開發中注意 API 的設計。TDD 測試驅動開發的思想就是在開發階段編寫可測試性程式碼,以保證程式碼的可測試性。
  • 保證重構:網際網路行業產品迭代速度很快,迭代後必然存在程式碼重構的過程,那怎麼才能保證重構後程式碼的質量呢?有測試用例做後盾,就可以大膽的進行重構

單元測試

單元測試包括:斷言,測試框架,測試用例,測試覆蓋率,mock 等幾個方面。目的是讓開發者明確知道程式碼結果

斷言

斷言:保證最小單元是否正常執行檢測方法。

測試框架

測試框架用於為測試服務,它本身並不參與測試,主要用於管理測試用例和生成測試報告,提升測試用例的開發速度、可讀性和可維護性。常見的測試框架有:mocha,jest,jasmine 等。

測試風格

通常有以下兩種測試風格,開發者可以根據情況選擇。

  • 測試驅動開發(TDD):關注所有的功能是否被實現,每一個功能都具備對應的測試用例。
  • 行為驅動開發(BDD):關注整體行為是否符合預期,編寫的每一行程式碼都有目的提供一個全面的測試用例集。
TDD 執行流程

TDD(Test Drived Develop)。TDD 的原理是在開發功能程式碼之前,先編寫單元測試用例程式碼,通過測試來推動整個開發的進行

  • setup(單個測試用例開始之前)
  • suiteSetup(每一個測試用例開始之前)
  • test(定義測試用例,並用斷言庫進行設定)
  • teardown(單個測試用例開始之後)
  • suiteTeardown(每一個測試用例開始之後)
suite('Array', function() {
  setup(function() {
    // 測試用例開始之前
  });
  suite('#indexOf', function() {
    test('should return -1 when not present', function() {
      assert.equal(-1, [1, 2, 3].indexOf(4));
    });
  });
  teardown(function() {
    // 測試用例之後
  });
});
複製程式碼
BDD 執行流程

BDD(Behavior Driven Development)。行為驅動的開發,描述了軟體的行為過程,可以直接作為軟體的需求文件

  • before(單個測試用例開始之前)
  • beforeEach(每一個測試用例開始之前)
  • it(定義測試用例,並用斷言庫進行設定)
  • after(單個測試用例開始之後)
  • afterEach(每一個測試用例開始之後)
describe('Array', function() {
  before(function() {
    // 測試用例開始之前
  });
  describe('#indexOf', function() {
    it('should return -1 when not present', function() {
      [1, 2, 3].indexOf(4).should.equal(-1);
    });
  });
});
複製程式碼

測試用例

測試用例(Test Case)是為某個特殊目標而編制的一組測試輸入、執行條件以及預期結果,用來判斷一個程式碼功能是否滿足特定需求。測試用例最小需要通過正向測試和反向測試來保證對功能的覆蓋。

使用 it 來定義一個測試用例:

it('should return -1 when not present', function(done) {
  // xxx
});
複製程式碼

非同步測試

在執行非同步測試用例時,會將 done 函式注入實參。

it('should return -1 when not present', function(done) {
  setTimeout(() => {
    done();
  }, 1000);
});
複製程式碼

測試覆蓋率

測試覆蓋率用來判斷單元測試對程式碼的覆蓋情況。原理是通過向原始碼中注入統計程式碼,用於監聽每一行程式碼的執行情況。可以統計到該行程式碼是否執行和執行次數。測試覆蓋率包括以下幾個方面:

  • 行覆蓋率(line coverage):是否每一行都執行了?
  • 函式覆蓋率(function coverage):是否每個函式都呼叫了?
  • 分支覆蓋率(branch coverage):是否每個 if 程式碼塊都執行了?
  • 語句覆蓋率(statement coverage):是否每個語句都執行了?

E2E 測試

E2E(End To End)測試是模擬使用者進行頁面操作,通過來判斷頁面狀態的變化,從而檢查功能是否執行正常的測試方法。

在使用 E2E 測試時,測試程式常常會自動開啟瀏覽器,執行配置的操作,因此需要驅動瀏覽器,常見的庫有:

  • selenium/webdriver
  • nightwatch
  • puppeteer

效能測試

效能測試的範疇比較廣泛,包括基準測試、壓力測試等。

基準測試

基準測試統計的是在多少時間內執行了多少次某個方法。常用 Benchmark 庫來編寫。

  • 面向切面程式設計(AOP)無侵入式統計。
  • Benchmark 基準測試方法,他並不是簡單地統計執行多少次測試程式碼後對比時間,它對測試有著嚴密的抽樣過程,執行多少次取決於取樣到的資料能否完成統計。根據統計次數計算方差。
var suite = new Benchmark.Suite();

// add tests
suite
  .add('RegExp#test', function() {
    /o/.test('Hello World!');
  })
  .add('String#indexOf', function() {
    'Hello World!'.indexOf('o') > -1;
  })
  // add listeners
  .on('cycle', function(event) {
    console.log(String(event.target));
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
  })
  // run async
  .run({ async: true });

// logs:
// => RegExp#test x 4,161,532 +-0.99% (59 cycles)
// => String#indexOf x 6,139,623 +-1.00% (131 cycles)
// => Fastest is String#indexOf
複製程式碼

壓力測試

壓力測試主要是針對於伺服器端,它可以幫助我們發現系統中的瓶頸問題,減少釋出到生產環境後出問題的機率。還能預估系統的承載能力,使我們能根據其做出一些應對措施。

對網路介面做壓力測試需要檢查的幾個常用指標有吞吐率,響應時間和併發數,這些指標反映了伺服器併發處理能力。

  • pv 網站當日訪問人數。
  • uv 獨立訪問人數。
  • qps 伺服器每秒處理請求數。qps=pv/t。

常用的壓力測試工具:ab、JMeter。

安全測試

安全測試是比較專業的測試,測試人員需要了解常見的 web 攻擊方式,然後模擬攻擊測試的程式,以防上線後其他不法人員使用同樣的方式對程式進行攻擊。前端常見的攻擊方式有:

  • XSS
  • SQL 注入
  • CSRF

功能測試

功能測試是對產品的各功能進行驗證,根據功能測試用例,逐項測試,檢查產品是否達到使用者要求的功能。常見的功能測試方法有冒煙測試、迴歸測試等。

冒煙測試

自由測試的一種,找到一個 bug 然後修復,然後專門針對此 bug。優缺點如下:

  • 節省時間。
  • 缺點覆蓋率極低。

迴歸測試

修改一處對整體功能全部測試,一般配合自動化測試。優缺點如下:

  • 大幅度降低出錯概率。
  • 時間耗費重。

測試相關庫

  • karma:使用真實的瀏覽器環境,並且可測試相容性。
  • mocha:測試框架。
  • chai:斷言庫。
  • istanbuljs/nyc:覆蓋率庫。
  • backstopjs:測試 UI。
  • benchmark:基準測試。
  • phantomjs:無頭瀏覽器。
  • nightwatch:e2e 測試。
  • jest:大而全。

更多文章,歡迎 Star 和 訂閱我的部落格

相關文章