關於重構ch.4-建構測試程式

為了正確的進行重構,需要一套可靠的測試工具來發現難以避免的錯誤

這篇筆記一併介紹測試種類,以及利用Jest&Peppeteer實作

Building Test-建構測試程式

章節分成七個部分:

  1. The value of Self-Testing Code-自檢程式的價值
  2. Sample Code to Test-測試的範例
  3. A First Test-初次測試
  4. Add Another Test-加入其他測試
  5. Modifying The Fixture-修改fixture
  6. Probing the Boundaries-探測邊界
  7. Much More Than This-點到為止?(淺嘗輒止)

The Value of Self-Testing Code

修復bug很快,但找出bug的原因像惡夢 / 修好一個bug還有千千萬萬個bug,而且不知道什麼時候發作

實現頻繁的測試,讓每一次的測試只相距幾分鐘,可以知道bug來自不久前寫的code,也因為記憶猶新,容易找出bug。

開始coding時就是寫測試的好時機!

先寫測試程式才實作功能的程式開發技巧稱為測試驅動開發(Test-Driven Development),透過Test/Coding/Refactor (Red–Green–Refactor)的循環。

Red-Green-Refactor

One of the most widely used techniques for code refactoring is the red/green process used in Agile test-driven development. Applying the Red-Green-Refactor method, developers break refactoring down into three distinct steps:

  1. think about what you want to develop. [RED]

  2. think about how to make your tests pass. [GREEN]

  3. think about how to improve your existing implentation. [REFACTOR]

    Red-Green Refactor

Sample Code to Test

利用Academind的影片的測試範例

範例下載網址

輸入Name&Age,點擊Add User會出現{}

example

點擊Add User會出現 {name} ({age} years old)

example-testing-result

A First Test-demo

開始寫測試之前…

Testing setup

we need some tools that help us with creating these tests

typically need three kinds of tools!

  • Test runners
    • test runner executes your tests and summarizes the results in the terminal.
    • e.g. Mocha / Jest
  • Assertion library
    • define testing logic, conditions
    • also need to be able to define your expect actions and ckeck them.
    • e.g. Chai
    • Jest is test runner+assertion library combined.
  • Headless browser (e2e-testing tools)
    • simulation browser interaction
    • e.g. Selenium/ Puppeteer

Jest

  • npm install –save-dev jest

    • 利用npm套件管理工具下載Jest

    • Jest will be development dependency of our project

    • //package.json
          
      "scripts":{
          "test":"jest"
          // "test":"jest --watch"	自動re-run   
      }
      
  • Jest會自動執行檔名結尾為 .spec.js或 .test.js的檔案


  • 初次測試generateText function

    // util.js 
    // 回傳name和age組合的字串
    exports.generateText = (name, age) => {
      // Returns output text
      return `${name} (${age} years old)`;
    };
    
  • 試著建立util.test.js

    // import欲測試的function (ex:generateText)
    const { generateText } = require('./util')
      
    //定義test function
    //傳入兩個參數:	1.測試的描述	2.一個包含測試邏輯的匿名函式
    //test('name', () => {
        // jest will execute to re-run
        //testing code
    //})
    test('should output name and age', () => {
      const text = generateText('Max', 29)
      expect(text).toBe('Max (29 years old)')
    })
    // 執行generateText('Max', 29)
    // expect function是由assertion library提供
    // 檢查text是否為預想的結果-'Max (29 years old)'
    // ex: tobe(5)確認值是否為5 	;toBeCalled function是否有被執行	;not.toBe(5)確認值不等於5
    

coding時至少都要看到每一個測試失敗一次,確保該失敗的時候失敗


Add Another Test & Modifying The Fixture & Probing the Boundaries

// util.js
exports.generateText = (name, age) => {
  // Returns output text
  return `${name} (${name} years old)`;
};
// util.test.js
test('should output name and age', () => {
    const text = generateText('Max', 29);
    expect(text).toBe('Max (29 years old)');
});

執行npm test

RED-exapmple

顯示結果-在”should output name and age”測試中,Received和Expected不同

測試當資料為空/0/負值等情形時

test('should output data-less text', () => {
    const text = generateText('', null);
    expect(text).toBe(' ( years old)')
})
// fail!!!

Puppeteer

  • e2e-tests需要安裝額外工具去控制browser

  • npm install –save-dev puppeteer

  • Puppeteer官網,Puppeteer github

  • 將每個步驟逐行輸入(開啟browser、開啟新頁面、導向至url、點擊輸入框、type something)

    const puppeteer = require('puppeteer')
      
    // 利用puppeteer.lanch啟用browser
    // browser物件:	newPage()開啟新頁面 ;goto()導向URL	;click()頁面點擊互動
    test('should create an element with text and correct class', async () => {
      const browser = await puppeteer.launch({
        headless: false,
        slowMo:80,// slow down by 80ms
        args:['--window-size=1920,1080']
      })
      // await 確保一個promise物件都resolve/reject才會繼續執行
      const page = await browser.newPage();
      await page.goto('localhost:4000/your-page');
      await page.click('input#name');
      await page.type('input#name', 'Anna');
      await page.click('input#age');
      await page.type('input#age', '28');
      await page.click('#btnAddUser');
      const finalText = await page.$eval('.user-item', el => el.textContent);
      expect(finalText).toBe('Anna (28 years old)');
    }, 10000)
    

所以關於測試這件事

內容取自JavaScript Testing Introduction Tutorial - Unit Tests, Integration Tests & e2e Tests

What is Testing?

writing an app and we are creating an application and we simply test it right

we had a feature , we open the browser and we test our application

what is Testing

[圖片來源: Academind]

we automate the testing part , so we automate it so that we don’t have to manually test everything in our application

Why test?

  • get an error if you break code
  • save time
  • think about possible issues & bugs
  • integrate into build workflow
  • break up complex dependencies
  • improve your code

Different kinds of tests(自動化測試)

Fully Isolated
(ex:testing one function)
Unit Tests write thousands of these!
With Dependencies
(e.g. testing a function that calls a function)
Integration Test write a good couple of these!
Full Flow
(e.g. validating a the DOM after a click)
End-to-End(E2E) Tests write a few of these!
  • 單元測試(Unit Testing)

    分為TDD(測試驅動開發)及BDD(行為驅動開發)

    測試框架: mocha, jasmine,ava,tap

  • 整合測試(Integration Testing)

  • 端對端測試(End-to-end Testing)

自動測試金字塔

[圖片來源: Academind]

Other types about Testing

different types of testing

​ 圖片來源:https://www.softwaretestinghelp.com/

延伸閱讀&圖文出處

Code Refactoring Best Practices: When (and When Not) to Do It

一次搞懂單元測試、整合測試、端對端測試之間的差異

Javascript Unit Test: Mocha, Chai, Sinon

使用 JEST 進行前端單元測試

The different types of software testing

Types Of Software Testing: Different Testing Types With Details

Academind-Javascript testing introduction