自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

通過構(gòu)建自己的JavaScript測試框架來了解JS測試

新聞 前端
在當今的軟件開發(fā)中,單元/功能測試已成為軟件開發(fā)的組成部分。隨著Nodejs的出現(xiàn),我們已經(jīng)看到了許多超級JS測試框架的發(fā)布:Jasmine,Jest等。

測試(單元或集成)是編程中非常重要的一部分。在當今的軟件開發(fā)中,單元/功能測試已成為軟件開發(fā)的組成部分。隨著Nodejs的出現(xiàn),我們已經(jīng)看到了許多超級JS測試框架的發(fā)布:Jasmine,Jest等。

單元測試框架

這有時也稱為隔離測試,它是測試獨立的小段代碼的實踐。如果你的測試使用某些外部資源(例如網(wǎng)絡(luò)或數(shù)據(jù)庫),則不是單元測試。

單元測試框架試圖以人類可讀的格式描述測試,以便非技術(shù)人員可以理解所測試的內(nèi)容。然而,即使你是技術(shù)人員,BDD格式的閱讀測試也會使你更容易理解所發(fā)生的事情。

例如,如果我們要測試此功能:

  1. function helloWorld() { 
  2.   return 'Hello world!'

我們會像這樣寫一個jasmine測試規(guī)范:

  1. describe('Hello world', () => { ① 
  2.   it('says hello', () => { ② 
  3.       expect(helloWorld())③.toEqual('Hello world!'); ④ 
  4.   }); 
  5. }); 

說明:

  1. describe(string, function) 
  2. it(string, function) 

安裝和拆卸

有時候為了測試一個功能,我們需要進行一些設(shè)置,也許是創(chuàng)建一些測試對象。另外,完成測試后,我們可能需要執(zhí)行一些清理活動,也許我們需要從硬盤驅(qū)動器中刪除一些文件。

這些活動稱為“設(shè)置和拆卸”(用于清理),Jasmine有一些功能可用來簡化此工作:

  • beforeAll 這個函數(shù)在describe測試套件中的所有規(guī)范運行之前被調(diào)用一次。
  • afterAll 在測試套件中的所有規(guī)范完成后,該函數(shù)將被調(diào)用一次。
  • beforeEach 這個函數(shù)在每個測試規(guī)范之前被調(diào)用, it 函數(shù)已經(jīng)運行。
  • afterEach 在運行每個測試規(guī)范之后調(diào)用此函數(shù)。

在Node中的使用

在Node項目中,我們在與 src 文件夾相同目錄的 test 文件夾中定義單元測試文件:

  1. node_prj 
  2.     src/ 
  3.         one.js 
  4.         two.js 
  5.     test/ 
  6.         one.spec.js 
  7.         two.spec.js 
  8.     package.json 

該測試包含規(guī)格文件,這些規(guī)格文件是src文件夾中文件的單元測試, package.json 在 script 部分進行了 test 。

  1.   ..., 
  2.   "script": { 
  3.       "test""jest" // or "jasmine" 
  4.     } 

如果 npm run test 在命令行上運行,則jest測試框架將運行 test 文件夾中的所有規(guī)范文件,并在命令行上顯示結(jié)果。

現(xiàn)在,我們知道了期望和構(gòu)建的內(nèi)容,我們繼續(xù)創(chuàng)建自己的測試框架。我們的這個框架將基于Node,也就是說,它將在Node上運行測試,稍后將添加對瀏覽器的支持。

我們的測試框架將包含一個CLI部分,該部分將從命令行運行。第二部分將是測試框架的源代碼,它將位于lib文件夾中,這是框架的核心。

首先,我們首先創(chuàng)建一個Node項目。

  1. mkdir kwuo 
  2. cd kwuo 
  3. npm init -y 

安裝chalk依賴項,我們將需要它來為測試結(jié)果上色: npm i chalk 。

創(chuàng)建一個lib文件夾,其中將存放我們的文件。

  1. mkdir lib 

我們創(chuàng)建一個bin文件夾是因為我們的框架將用作Node CLI工具。

  1. mkdir bin 

首先創(chuàng)建CLI文件。

在bin文件夾中創(chuàng)建kwuo文件,并添加以下內(nèi)容:

  1. #!/usr/bin/env node 
  2.  
  3. process.title = 'kwuo' 
  4. require('../lib/cli/cli'

我們將hashbang設(shè)置為指向 /usr/bin/env node,這樣就可以在不使用node命令的情況下運行該文件。

我們將process的標題設(shè)置為“kwuo”,并要求文件“lib/cli/cli”,這樣就會調(diào)用文件cli.js,從而啟動整個測試過程。

現(xiàn)在,我們創(chuàng)建“lib/cli/cli.js”并填充它。

  1. mkdir lib/cli 
  2. touch lib/cli/cli.js 

該文件將搜索測試文件夾,在“test”文件夾中獲取所有測試文件,然后運行測試文件。

在實現(xiàn)“lib/cli/cli.js”之前,我們需要設(shè)置全局變量。

測試文件中使用了describe,beforeEach,beforeEach,afterAll,beforeAll函數(shù):

  1. describe('Hello world', () => {  
  2.   it('says hello', () => {  
  3.     expect(helloWorld()).toEqual('Hello world!'); 
  4.   }); 
  5. }); 

但是在測試文件中都沒有定義。沒有ReferenceError的情況下文件和函數(shù)如何運行?因為測試框架在運行測試文件之前,會先實現(xiàn)這些函數(shù),并將其設(shè)置為globals,所以測試文件調(diào)用測試框架已經(jīng)設(shè)置好的函數(shù)不會出錯。而且,這使測試框架能夠收集測試結(jié)果并顯示失敗或通過的結(jié)果。

讓我們在lib文件夾中創(chuàng)建一個 index.js 文件:

  1. touch lib/index.js 

在這里,我們將設(shè)置全局變量并實現(xiàn) describe , it , expectEach , beforeEach , afterAll , beforeAll 函數(shù)。

  1. // lib/index.js 
  2.  
  3. const chalk = require('chalk'
  4. const log = console.log 
  5. var beforeEachs = [] 
  6. var afterEachs = [] 
  7. var afterAlls = [] 
  8. var beforeAlls = [] 
  9. var Totaltests = 0 
  10. var passedTests = 0 
  11. var failedTests = 0 
  12. var stats = [] 
  13. var currDesc = { 
  14.   it: [] 
  15.  
  16. var currIt = {} 
  17.  
  18. function beforeEach(fn) { 
  19.   beforeEachs.push(fn) 
  20.  
  21. function afterEach(fn) { 
  22.   afterEachs.push(fn) 
  23.  
  24. function beforeAll(fn) { 
  25.   beforeAlls.push(fn) 
  26.  
  27. function afterAll(fn) { 
  28.   afterAlls.push(fn) 
  29.  
  30. function expect(value) { 
  31.   return { 
  32.  
  33.     // Match or Asserts that expected and actual objects are same. 
  34.     toBe: function(expected) { 
  35.       if (value === expected) { 
  36.         currIt.expects.push({ name: `expect ${value} toBe ${expected}`, status: true }) 
  37.         passedTests++ 
  38.       } else { 
  39.         currIt.expects.push({ name: `expect ${value} toBe ${expected}`, status: false }) 
  40.         failedTests++ 
  41.       } 
  42.     }, 
  43.  
  44.     // Match the expected and actual result of the test. 
  45.     toEqual: function(expected) { 
  46.       if (value == expected) { 
  47.         currIt.expects.push({ name: `expect ${value} toEqual ${expected}`, status: true }) 
  48.         passedTests++ 
  49.       } else { 
  50.         currIt.expects.push({ name: `expect ${value} toEqual ${expected}`, status: false }) 
  51.         failedTests++ 
  52.       } 
  53.     } 
  54.   } 
  55.  
  56. function it(desc, fn) { 
  57.   Totaltests++ 
  58.   if (beforeEachs) { 
  59.     for (var index = 0; index < beforeEachs.length; index++) { 
  60.       beforeEachs[index].apply(this
  61.     } 
  62.   } 
  63.   //var f = stats[stats.length - 1] 
  64.   currIt = { 
  65.     name: desc, 
  66.     expects: [] 
  67.   } 
  68.   //f.push(desc) 
  69.   fn.apply(this
  70.   for (var index = 0; index < afterEachs.length; index++) { 
  71.     afterEachs[index].apply(this
  72.   } 
  73.   currDesc.it.push(currIt) 
  74.  
  75. function describe(desc, fn) { 
  76.   currDesc = { 
  77.     it: [] 
  78.   } 
  79.   for (var index = 0; index < beforeAlls.length; index++) { 
  80.     beforeAlls[index].apply(this
  81.   } 
  82.   currDesc.name = desc 
  83.   fn.apply(this
  84.   for (var index = 0; index < afterAlls.length; index++) { 
  85.     afterAlls[index].apply(this
  86.   } 
  87.   stats.push(currDesc) 
  88.  
  89. exports.showTestsResults = function showTestsResults() { 
  90.     console.log(`Total Test: ${Totaltests}     
  91. Test Suites: passed, total 
  92. Tests: ${passedTests} passed, ${Totaltests} total 
  93. `) 
  94.   const logTitle = failedTests > 0 ? chalk.bgRed : chalk.bgGreen 
  95.   log(logTitle('Test Suites')) 
  96.   for (var index = 0; index < stats.length; index++) { 
  97.     var e = stats[index]; 
  98.     const descName = e.name 
  99.     const its = e.it 
  100.     log(descName) 
  101.     for (var i = 0; i < its.length; i++) { 
  102.       var _e = its[i]; 
  103.       log(`   ${_e.name}`) 
  104.       for (var ii = 0; ii < _e.expects.length; ii++) { 
  105.         const expect = _e.expects[ii] 
  106.         log(`      ${expect.status === true ? chalk.green('√') : chalk.red('X') } ${expect.name}`) 
  107.       } 
  108.     } 
  109.     log() 
  110.   } 
  111.  
  112. global.describe = describe 
  113. global.it = it 
  114. global.expect = expect 
  115. global.afterEach = afterEach 
  116. global.beforeEach = beforeEach 
  117. global.beforeAll = beforeAll 
  118. global.afterAll = afterAll 

在開始的時候,我們需要使用chalk庫,因為我們要用它來把失敗的測試寫成紅色,把通過的測試寫成綠色。我們將 console.log 縮短為 log。

接下來,我們設(shè)置beforeEachs,afterEachs,afterAlls,beforeAlls的數(shù)組。beforeEachs將保存在它所附加的 it 函數(shù)開始時調(diào)用的函數(shù);afterEachs將在它所附加的 it 函數(shù)的末尾調(diào)用;beforeEachs和afterEachs分別在 describe 函數(shù)的開始和結(jié)尾處調(diào)用。

我們設(shè)置了 Totaltests 來保存運行的測試數(shù)量, passTests 保存已通過的測試數(shù), failedTests 保存失敗的測試數(shù)。

stats 收集每個describe函數(shù)的stats, curDesc 指定當前運行的describe函數(shù)來幫助收集測試數(shù)據(jù), currIt 保留當前正在執(zhí)行的 it 函數(shù),以幫助收集測試數(shù)據(jù)。

我們設(shè)置了beforeEach、afterEach、beforeAll和afterAll函數(shù),它們將函數(shù)參數(shù)推入相應(yīng)的數(shù)組,afterAll推入afterAlls數(shù)組,beforeEach推入beforeEachs數(shù)組,等等。

接下來是expect函數(shù),此函數(shù)進行測試:

  1. expect(56).toBe(56// 經(jīng)過測試56預期會是56 
  2. expect(func()).toEqual("nnamdi"// 該函數(shù)將返回一個等于“nnamdi”的字符串 

expect 函數(shù)接受一個要測試的參數(shù),并返回一個包含匹配器函數(shù)的對象。在這里,它返回一個具有 toBe 和 toEqual 函數(shù)的對象,它們具有期望參數(shù),用于與expect函數(shù)提供的value參數(shù)匹配。 toBe 使用 === 將value參數(shù)與期望參數(shù)匹配, toEqual 使用 == 測試期望值。如果測試通過或失敗,則這些函數(shù)將遞增 passedTests 和 failedTests 變量,并且還將統(tǒng)計信息記錄在currIt變量中。

我們目前只有兩個matcher函數(shù),還有很多:

  • toThrow
  • toBeNull
  • toBeFalsy
  • etc

你可以搜索它們并實現(xiàn)它們。

接下來,我們有 it 函數(shù), desc 參數(shù)保存測試的描述名稱,而 fn 保存函數(shù)。它先對beforeEachs進行fun,設(shè)置統(tǒng)計,調(diào)用 fn 函數(shù),再調(diào)用afterEachs。

describe 函數(shù)的作用和 it 一樣,但在開始和結(jié)束時調(diào)用 beforeAlls 和 afterAlls 。

showTestsResults 函數(shù)通過 stats 數(shù)組進行解析,并在終端上打印通過和失敗的測試。

我們實現(xiàn)了這里的所有函數(shù),并將它們都設(shè)置為全局對象,這樣才使得測試文件調(diào)用它們時不會出錯。

回到“lib/cli/cli.js”:

  1. // lib/cli/cli.js 
  2. const path = require('path'
  3. const fs = require('fs'
  4. const { showTestsResults } = require('./../'

首先,它從“lib/index”導入函數(shù) showTestsResult ,該函數(shù)將在終端顯示運行測試文件的結(jié)果。另外,導入此文件將設(shè)置全局變量。

讓我們繼續(xù):

run 函數(shù)是這里的主要函數(shù),這里調(diào)用它,可以引導整個過程。它搜索 test 文件夾 searchTestFolder ,然后在數(shù)組 getTestFiles 中獲取測試文件,它循環(huán)遍歷測試文件數(shù)組并運行它們 runTestFiles 。

  • searchTestFolder :使用 fs#existSync 方法檢查項目中是否存在“test/”文件夾。
  • getTestFiles :此函數(shù)使用 fs#readdirSync 方法讀取“test”文件夾的內(nèi)容并返回它們。
  • runTestFiles :它接受數(shù)組中的文件,使用 forEach 方法循環(huán)遍歷它們,并使用 require方法運行每個文件。

kwuo文件夾結(jié)構(gòu)如下所示:

測試我們的框架

我們已經(jīng)完成了我們的測試框架,讓我們通過一個真實的Node項目對其進行測試。

我們創(chuàng)建一個Node項目:

  1. mkdir examples 
  2. mkdir examples/math 
  3. cd examples/math 
  4. npm init -y 

創(chuàng)建一個src文件夾并添加add.js和sub.js

  1. mkdir src 
  2. touch src/add.js src/sub.js 

add.js和sub.js將包含以下內(nèi)容:

  1. // src/add.js 
  2. function add(a, b) { 
  3.     return a+b 
  4.  
  5. module.exports = add 
  6.  
  7. // src/sub.js 
  8. function sub(a, b) { 
  9.     return a-b 
  10.  
  11. module.exports = sub 

我們創(chuàng)建一個測試文件夾和測試文件:

  1. mkdir test 
  2. touch test/add.spec.js test/sub.spec.js 

規(guī)范文件將分別測試add.js和sub.js中的add和sub函數(shù)

  1. // test/sub.spec.js 
  2. const sub = require('../src/sub'
  3. describe("Subtract numbers", () => { 
  4.   it("should subtract 1 from 2", () => { 
  5.     expect(sub(21)).toEqual(1
  6.   }) 
  7.    
  8.   it("should subtract 2 from 3", () => { 
  9.     expect(sub(32)).toEqual(1
  10.   }) 
  11. }) 
  12.  
  13. // test/add.spec.js 
  14. const add = require('../src/add'
  15. describe("Add numbers", () => { 
  16.   it("should add 1 to 2", () => { 
  17.     expect(add(12)).toEqual(3
  18.   }) 
  19.    
  20.   it("should add 2 to 3", () => { 
  21.     expect(add(23)).toEqual(5
  22.   }) 
  23. }) 
  24. describe('Concat Strings', () => { 
  25.   let expected; 
  26.   beforeEach(() => { 
  27.     expected = "Hello"
  28.   }); 
  29.    
  30.   afterEach(() => { 
  31.     expected = ""
  32.   }); 
  33.    
  34.   it('add Hello + World', () => { 
  35.     expect(add("Hello""World")) 
  36.       .toEqual(expected); 
  37.   }); 
  38. }); 

現(xiàn)在,我們將在package.json的“script”部分中運行“test”以運行我們的測試框架:

  1.   "name""math"
  2.   "version""1.0.0"
  3.   "description"""
  4.   "main""index.js"
  5.   "scripts": { 
  6.     "test""kwuo" 
  7.   }, 
  8.   "keywords": [], 
  9.   "author""Chidume Nnamdi <kurtwanger40@gmail.com>"
  10.   "license""ISC" 

我們在命令行上運行 npm run test ,結(jié)果將是這樣的:

看,它給我們展示了統(tǒng)計數(shù)據(jù),通過測試的總數(shù),以及帶有“失敗”或“通過”標記的測試套件列表??吹酵ㄟ^的測試期望“add Hello + World”,它將返回“HelloWorld”,但我們期望返回“Hello”。如果我們糾正它并重新運行測試,所有測試都將通過。

  1. // test/add.spec.js 
  2. ... 
  3. describe('Concat Strings', () => { 
  4.   let expected; 
  5.   beforeEach(() => { 
  6.     expected = "Hello"
  7.   }); 
  8.    
  9.   afterEach(() => { 
  10.     expected = ""
  11.   }); 
  12.    
  13.   it('add Hello + World', () => { 
  14.     expect(add("Hello""")) 
  15.       .toEqual(expected); 
  16.   }); 
  17. }); 

 

看,我們的測試框架像Jest和Jasmine一樣工作。它僅在Node上運行,在下一篇文章中,我們將使其在瀏覽器上運行。

代碼在Github上

Github倉庫地址: philipszdavido/kwuoKwuo

你可以使用來自NPM的框架:

  1. cd IN_YOUR_NODE_PROJECT 
  2. npm install kwuo -D 

將package.json中的“test”更改為此:

  1.   ... 
  2.   "scripts": { 
  3.     "test""kwuo" 
  4.     ... 
  5.   } 

總結(jié)

我們建立了我們的測試框架,在這個過程中,我們學會了如何使用全局來設(shè)置函數(shù)和屬性在運行時任何地方可見。

我們看到了如何在項目中使用 describe 、 it 、 expect 和各種匹配函數(shù)來運行測試。下一次,你使用Jest或Jasmine,你會更有信心,因為現(xiàn)在你知道它們是如何工作的。

 

 

責任編輯:張燕妮 來源: segmentfault
相關(guān)推薦

2022-01-06 22:04:03

JavaScript語言開發(fā)

2023-01-31 16:35:34

JavaScript測試框架

2012-12-18 13:32:45

IBMdW

2023-09-13 11:40:12

2009-07-22 14:49:18

ibmdwPython測試

2023-11-08 13:18:00

JestJavaScript框架

2011-06-03 17:06:09

自動化測試

2022-05-24 07:51:05

測試模型測試單元測試

2011-10-08 13:45:12

JavaScript

2020-06-24 07:44:45

JavaScript開發(fā)代碼

2017-08-10 14:04:25

前端JavaScript函數(shù)性能

2020-01-10 15:57:03

JavaScript開發(fā) 技巧

2020-01-10 10:48:27

JavaScript框架StateOfJS

2015-09-06 10:58:36

PHP框架搭建結(jié)構(gòu)

2017-05-16 16:28:21

互聯(lián)網(wǎng)

2009-06-22 15:52:15

JSF測試框架

2011-03-30 16:54:13

JUnit

2016-11-30 18:35:03

JavaScript

2021-08-17 09:00:00

架構(gòu)PythonWeb

2022-05-12 09:37:03

測試JUnit開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號