成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

編寫可測試的JavaScript代碼

移動開發(fā)
無論我們使用和Node配合在一起的測試框架,例如Mocha或者Jasmine,還是在像PhantomJS這樣的無頭瀏覽器中運行依賴于DOM的測試,和以前相比,我們有更好的方式來對JavaScript進行單元測試。

無論我們使用和Node配合在一起的測試框架,例如Mocha或者Jasmine,還是在像PhantomJS這樣的無頭瀏覽器中運行依賴于DOM的測試,和以前相比,我們有更好的方式來對JavaScript進行單元測試。

然而,這并不意味著我們要測試的代碼就像我們的工具那樣容易!組織和編寫易于測試的代碼需要花費一些精力和并對其進行規(guī)劃,但是在函數(shù)式編程的啟發(fā)下,我們發(fā)現(xiàn)了一些模式,當(dāng)我們需要測試我們的代碼時,這些模式可以幫助我們避免那些“坑”。在這篇文章中,我們會查看一些有用的小貼士和模式,來幫助我們在JavaScript中編寫可測試的代碼。

保持業(yè)務(wù)邏輯和顯示邏輯分離

對于基于JavaScript的瀏覽器應(yīng)用程序來說,其中一項主要工作就是監(jiān)聽終端用戶觸發(fā)的DOM事件,然后通過運行一些業(yè)務(wù)邏輯并在頁面上顯示結(jié)果,以此對用戶做出反饋。在建立DOM事件監(jiān)聽器的地方,有時會誘惑你編寫一個匿名函數(shù)來完成所有這些工作。這樣帶來的問題是為了測試匿名函數(shù),你不得不去模擬DOM事件。這樣不僅會增加代碼行數(shù),而且會增加測試運行的時間。 

 

 

 

與之相反,編寫一個有名字的函數(shù),然后將其傳給事件處理器。通過這種方式,你可以直接針對這個有名字的函數(shù)編寫測試用例,而不用去觸發(fā)一個假的DOM事件。

這不僅僅可以應(yīng)用到DOM上。在瀏覽器和Node上的很多API,都被設(shè)計成觸發(fā)和監(jiān)聽事件,或者等待其它類型的異步工作完成。憑經(jīng)驗說,如果你編寫了大量的匿名回調(diào)函數(shù),那么你的代碼可能不會容易被測試。

  1. // hard to test 
  2. $('button').on('click', () => { 
  3.     $.getJSON('/path/to/data'
  4.         .then(data => { 
  5.             $('#my-list').html('results: ' + data.join(', ')); 
  6.         }); 
  7. }); 
  8.   
  9. // testable; we can directly run fetchThings to see if it 
  10. // makes an AJAX request without having to trigger DOM 
  11. // events, and we can run showThings directly to see that it 
  12. // displays data in the DOM without doing an AJAX request 
  13. $('button').on('click', () => fetchThings(showThings)); 
  14.   
  15. function fetchThings(callback) { 
  16.     $.getJSON('/path/to/data').then(callback); 
  17.   
  18. function showThings(data) { 
  19.     $('#my-list').html('results: ' + data.join(', ')); 
  20.  

對異步代碼使用回調(diào)或者Promise

在上述的示例代碼中,我們經(jīng)過重構(gòu)的函數(shù)fetchThings會運行一個AJAX請求,以異步的方式完成了大部分工作。這意味著我們不能運行函數(shù)并測試它是否按照我們預(yù)期的那樣運行,因為我們不知道它什么時候運行完。

解決這個問題最常見的方法,是向函數(shù)中傳遞一個回調(diào)函數(shù)作為參數(shù),作為異步調(diào)用。這樣,在你的單元測試中,你可以在傳遞的回調(diào)函數(shù)中運行一些斷言。 

 

 

 

另外一種常見并且越來越流行的組織異步代碼方法,是使用Promise API的方式。幸運的是,$.ajax和其它大部分jQuery的異步函數(shù)已經(jīng)返回了Promise對象,因此它已經(jīng)涵蓋了大部分常見的用例。

  1. // 很難測試;我們不知道AJAX請求會運行多長時間 
  2.   
  3. function fetchData() { 
  4.     $.ajax({ url: '/path/to/data' }); 
  5.   
  6. //可測試的;我們傳入一個回調(diào)函數(shù),然后在其中運行斷言 
  7.   
  8. function fetchDataWithCallback(callback) { 
  9.     $.ajax({ 
  10.         url: '/path/to/data'
  11.         success: callback, 
  12.     }); 
  13.   
  14. //同樣可測試的:在返回的Promise解析完后,我們可以運行斷言 
  15.   
  16. function fetchDataWithPromise() { 
  17.     return $.ajax({ url: '/path/to/data' }); 
  18.  

避免副作用

要編寫那些使用參數(shù)并且返回值僅僅依賴那些參數(shù)的函數(shù),就像將數(shù)字傳入數(shù)學(xué)公式,然后取得結(jié)果。如果你的函數(shù)依賴于一些外部的狀態(tài)(例如類實例的屬性或者某些文件的內(nèi)容),那么你在測試這個函數(shù)之前,就不得不去設(shè)置一些狀態(tài),在測試用例中需要更多的設(shè)置。你不得不去認為那些正在運行的代碼不會修改同一個的狀態(tài)。 

 

 

 

同樣,你需要避免編寫那些會修改外部狀態(tài)的函數(shù),例如向文件寫入內(nèi)容或者向數(shù)據(jù)庫保存數(shù)據(jù)。這會避免一些副作用,來影響你測試其他代碼的能力。一般來說,***是將副作用和代碼控制在一起,讓“表面積”盡可能小。對于類和對象實例來說,類方法的副作用應(yīng)該被限制在被測試的類實例的范圍內(nèi)。

  1. // 很難測試;我們不得不設(shè)置一個globalListOfCars對象和一個名為#list-of-models的DOM結(jié)構(gòu),然后才能測試這段代碼 
  2.   
  3. function processCarData() { 
  4.     const models = globalListOfCars.map(car => car.model); 
  5.     $('#list-of-models').html(models.join(', ')); 
  6.   
  7. // 容易測試;我們傳遞一個參數(shù)然后測試它的返回值,而不需要設(shè)置任何全局變量或者檢查任何DOM結(jié)果 
  8.   
  9. function buildModelsString(cars) { 
  10.     const models = cars.map(car => car.model); 
  11.     return models.join(','); 
  12.  

使用依賴注入

在函數(shù)中,有一種通用的模式,可以用來降低對外部狀態(tài)的使用,這就是依賴注入 —— 將函數(shù)的所有外部需要都通過函數(shù)參數(shù)的方式傳遞給函數(shù)。

  1. // 依賴于一個外部狀態(tài)數(shù)據(jù)連接實例;很難測試 
  2.   
  3. function updateRow(rowId, data) { 
  4.     myGlobalDatabaseConnector.update(rowId, data); 
  5.   
  6. // 將數(shù)據(jù)庫連接實例作為參數(shù)傳遞給函數(shù);很容易測試。 
  7.   
  8. function updateRow(rowId, data, databaseConnector) { 
  9.     databaseConnector.update(rowId, data); 
  10.  

使用依賴注入的一個主要好處,是你可以在單元測試中傳入mock對象,這樣就不會導(dǎo)致真的副作用(在這個例子中,就是更新數(shù)據(jù)庫行),你只需要斷言你的mock對象是按照期望的方式運行即可。

為每一個函數(shù)設(shè)置一個唯一的目的

將長函數(shù)分解成一系列小的、單一職責(zé)的函數(shù)。這樣我們可以更容易的去測試每一個函數(shù)是否是正確的,而不再希望一個大函數(shù)在返回結(jié)果之前就正確的做了所有的事情。

在函數(shù)式編程中,將幾個單一職責(zé)的函數(shù)拼在一起的行為稱作“組合”。Underscore.js甚至有一個名為_.compose的函數(shù),它將一個函數(shù)列表中的函數(shù)串在一起,將每一函數(shù)的返回結(jié)果作為輸入傳遞給下一個函數(shù)。

  1. // 很難測試 
  2.   
  3. function createGreeting(name, location, age) { 
  4.     let greeting; 
  5.     if (location === 'Mexico') { 
  6.         greeting = '!Hola'
  7.     } else { 
  8.         greeting = 'Hello'
  9.     } 
  10.   
  11.     greeting += ' ' + name.toUpperCase() + '! '
  12.   
  13.     greeting += 'You are ' + age + ' years old.'
  14.   
  15.     return greeting; 
  16.   
  17. // 很容易測試 
  18.   
  19. function getBeginning(location) { 
  20.     if (location === 'Mexico') { 
  21.         return '¡Hola'
  22.     } else { 
  23.         return 'Hello'
  24.     } 
  25.   
  26. function getMiddle(name) { 
  27.     return ' ' + name.toUpperCase() + '! '
  28.   
  29. function getEnd(age) { 
  30.     return 'You are ' + age + ' years old.'
  31.   
  32. function createGreeting(name, location, age) { 
  33.     return getBeginning(location) + getMiddle(name) + getEnd(age); 
  34.  

不要改變參數(shù)

在JavaScript中,數(shù)組和對象傳遞的是引用,而非值,因此它們是可變的。這意味著當(dāng)你將對象或者數(shù)組作為參數(shù)傳遞給函數(shù)時,你的代碼和使用你傳遞的對象或數(shù)組的函數(shù),都有能力去修改內(nèi)存中同一個數(shù)組或者對象。這意味著當(dāng)你測試你自己的代碼時,你必須信任所有你調(diào)用的函數(shù)中,沒有任何函數(shù)會修改你的對象。每當(dāng)你添加一些新的可以修改同一個對象的代碼時,跟蹤對象應(yīng)該是什么樣子就會變得越來越困難,從而更難去測試它們。 

 

 

[[177541]] 

相反,當(dāng)你有一個函數(shù)需要使用對象或者數(shù)組時,你應(yīng)該在代碼中對待對象或者數(shù)組就像它們是只讀的。你可以根據(jù)需要創(chuàng)建新的對象或者數(shù)組,然后對齊填充。或者,使用Undersocre或者Lodash去對傳入的對象或者數(shù)組做一個拷貝,然后再對齊進行操作。更好的選擇是,使用一些像Immutable.js這樣的工具,去創(chuàng)建只讀的數(shù)據(jù)結(jié)構(gòu)。

  1. // 修改了傳入的對象 
  2.   
  3. function upperCaseLocation(customerInfo) { 
  4.     customerInfo.location = customerInfo.location.toUpperCase(); 
  5.     return customerInfo; 
  6.   
  7. // 返回了一個新的對象 
  8.   
  9. function upperCaseLocation(customerInfo) { 
  10.     return { 
  11.         name: customerInfo.name
  12.         location: customerInfo.location.toUpperCase(), 
  13.         age: customerInfo.age 
  14.     }; 
  15.  

在編碼之前先寫測試

在編碼之前先寫單元測試的過程被稱作測試驅(qū)動開發(fā)(TDD)。大量的開發(fā)者發(fā)現(xiàn)TDD非常有用。

通過先編寫測試用例,你就強迫自己從使用你代碼的開發(fā)者角度來考慮你要暴露的API,它還幫助你確保你只會編寫足夠的代碼來滿足測試用例的要求,而不要對解決方案“過度施工”,從而帶來不必要的復(fù)雜性。

在實踐中,TDD作為一條紀律,要覆蓋所有的代碼改動可能會比較困難。但是當(dāng)它看上去值得嘗試的時候,這就是一個很好的方式來保證你的所有代碼都是可測試的。

總結(jié)

在編寫和測試復(fù)雜的JavaScript應(yīng)用的時候,我們都知道有一些很容易遇到的“陷阱”,但我希望通過這些貼士和提醒,可以讓我們的代碼盡量簡單和函數(shù)化,我們可以做到讓測試覆蓋率很高,讓整體的代碼復(fù)雜性很低!

責(zé)任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2013-04-15 09:02:43

JavaScriptJS

2012-12-17 13:51:22

Web前端JavaScriptJS

2022-06-07 09:30:35

JavaScript變量名參數(shù)

2014-04-21 10:14:52

PromisesJavaScript

2009-06-24 15:00:39

Javascript代

2023-01-27 14:53:03

2011-11-23 09:06:00

2011-03-04 10:11:09

JavascriptAPI

2021-10-10 23:02:49

Golang語言代碼

2018-11-08 15:50:18

前端Javascript重用性

2022-06-21 12:27:12

JavaScript前端

2011-12-29 15:02:27

JavaScript

2022-08-02 10:33:11

JavaScript代碼

2024-01-30 08:54:05

JavaScript技巧代碼

2012-09-17 10:35:41

JavaScriptJS代碼

2023-09-11 11:05:49

軟件開發(fā)TDD

2015-08-27 13:11:18

JavaScript代碼

2025-06-11 03:22:00

JavaScript開發(fā)前端

2022-08-28 19:03:18

JavaScript編程語言開發(fā)

2022-05-10 10:28:21

JavaScript代碼
點贊
收藏

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

主站蜘蛛池模板: 久久成人18免费网站 | 中文字幕在线视频免费视频 | 国产一区在线看 | 精品久久一区 | 自拍偷拍精品 | 久久综合影院 | 午夜视频一区二区三区 | av毛片在线免费观看 | 亚洲九九色 | 99精品99 | 一区二区三区精品在线视频 | 97视频在线观看网站 | 日韩午夜电影在线观看 | 一区二区不卡视频 | 99免费在线观看 | 亚洲91| 成人黄色电影在线观看 | 自拍 亚洲 欧美 老师 丝袜 | 全免费a级毛片免费看视频免 | 国产专区在线 | 一区二区视频 | 美国a级毛片免费视频 | 亚洲天堂av在线 | 免费视频一区二区 | 夜夜久久| 久久国产一区二区 | 成人精品在线视频 | 精品综合久久久 | 国产亚洲一级 | 天天躁日日躁性色aⅴ电影 免费在线观看成年人视频 国产欧美精品 | 国内精品一区二区三区 | 中文字幕不卡在线观看 | 国产成人麻豆免费观看 | 成人在线免费视频观看 | av福利网站 | 国产激情第一页 | 亚洲精品久久久久久久久久吃药 | 97av| 日韩精品久久一区二区三区 | 欧产日产国产精品v | 久久久www成人免费精品 |