原文出處 :AJAX單元測試傻瓜教程css
Ajax 請求常常容易發生錯誤,客戶端發送的數據出問題,服務器端返回的數據有誤都會致使 Ajax 請求錯誤。你不能保證與服務器的鏈接老是工做正常。Ajax請求須要將用戶的輸入發送給服務器並返回服務器響應,所以,對於數據的正確處理相當重要。
可是因爲Ajax是異步的,測試它的同時必須保證獨立性,咱們如何才能在Ajax與服務器進行通訊的時候對其進行單元測試呢?
不要怕,讓咱們看一看接下來的例子,學習一下如何對Ajax請求進行單元測試。html
在咱們開始單元測試以前,咱們須要安裝幾個必須的工具。node
新建一個目錄,用來存放必要的文件npm
使用 npm install mocha chai sinon
安裝 Mocha
, Chai
, Sinon
json
爲了使事情簡單,咱們將直接在瀏覽器中運行測試。若是你更喜歡基於命令行的測試的話,測試運行的結果也將會和瀏覽器中的結果徹底一致。
咱們將會使用如下的文件做爲測試運行器。我將其命名爲test.html
。數組
<!DOCTYPE html> <html> <head> <title>Mocha Tests</title> <link rel="stylesheet" href="node_modules/mocha/mocha.css"> </head> <body> <div id="mocha"></div> <script src="node_modules/mocha/mocha.js"></script> <script src="node_modules/sinon/pkg/sinon-1.12.2.js"></script> <script src="node_modules/chai/chai.js"></script> <script>mocha.setup('bdd')</script> <script src="testApi.js"></script> <script src="test.js"></script> <script> mocha.run(); </script> </body> </html>
注意mocha.js
,mocha.css
,sinon-1.17.1.js
,chai.js
的文件路徑。由於我是用npm
安裝它們的,因此它們都在node_modules
目錄下。對於 Sinon
,你可能須要更改文件名來匹配安裝的版本,我在這裏使用的是版本是1.17.1。同時也注意testApi.js
,test.js
這兩個文件,這兩個文件做爲個人實例模塊和測試用例,我將會在接下來逐一介紹它們。瀏覽器
接下來,咱們將建立一個基礎的模塊,該模塊將會發送一些Ajax請求。咱們將用它來向大家展現如何對Ajax進行單元測試。
咱們將該文件命名爲testApi.js
。服務器
var testApi = { get: function(callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://jsonplaceholder.typicode.com/posts/1', true); xhr.onreadystatechange = function() { if(xhr.readyState == 4) { if(xhr.status == 200) { callback(null, JSON.parse(xhr.responseText)); } else { callback(xhr.status); } } }; xhr.send(); }, post: function(data, callback) { var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://jsonplaceholder.typicode.com/posts', true); xhr.onreadystatechange = function() { if(xhr.readyState == 4) { callback(); } }; xhr.send(JSON.stringify(data)); } };
這段代碼開起來應該很熟悉,咱們寫了兩個函數,一個函數中使用GET
方法獲取數據,另外一個函數中使用POST
方法向服務器發送數據,這些都是最普通的 Ajax請求。咱們在這裏使用了JSONPlaceholder API,它是一個免費的在線REST服務,你可使用它提供的模擬數據,很是適合快速測試。網絡
以後咱們須要建立一個框架,在裏面添加每一個情景的測試集。該文件被命名爲test.js
。框架
chai.should(); describe('TestAPI', function() { //Tests etc. go here });
Mocha
中使用describe
來建立一個測試用例,該測試用例即是咱們添加測試代碼的地方。chai.should()
將容許咱們使用 "should style" 斷言。這意味着咱們能夠輕鬆的驗證咱們的測試結果,如:someValue.should.equal(12345)
。
咱們的示例模塊中有一個get
函數,該函數能夠被用來加載服務器傳送過來的數據。咱們將會建立一個測試函數來證實由服務器取到數據是一個JSON數據。可是該GET
請求是由XMLHttpRequest
發出的,因爲咱們測試的時候並無服務器接收咱們的數據,因此咱們不能真的發出一個 Http 請求,那麼咱們如何才能避免真的發出請求呢?又如何能獲得返回的數據呢?
彆着急,這個時候就到了 Sinon 出場了。咱們一開始在就在test.html
中引用了 Sinon 的庫,有了 Sinon,咱們就能夠模擬服務器響應。咱們能夠將XMLHttpRequest用一個替身來代替,咱們稱之爲 fake XMLHttpRequest,這樣咱們就能在測試中輕鬆控制Ajax請求了。
咱們須要稍微更新一下咱們的測試用例框架,如下是更改以後的test.js
文件。
chai.should(); describe('TestAPI', function() { beforeEach(function() { this.xhr = sinon.useFakeXMLHttpRequest(); this.requests = []; this.xhr.onCreate = function(xhr) { this.requests.push(xhr); }.bind(this); }); afterEach(function() { this.xhr.restore(); }); //Tests etc. go here });
beforeEach
和afterEach
就像他們的名字同樣,將會在每一次的測試前和測試後被調用。在每個測試以前,咱們實例化了一個 fake XMLHttpRequest 而且將每個被建立的 fake request 放入了一個數組中,這些值被存儲在this.xhr
和this.request
中,這樣咱們就能夠在該測試中的其餘函數中使用了。
在每一次測試以後,咱們使用了this.xhr.restore
來恢復初始的XMLHttpRequest對象。
如今,咱們能夠開始在test.js
中寫下咱們的第一個測試集:
it('should parse fetched data as JSON', function(done) { var data = { foo: 'bar' }; var dataJson = JSON.stringify(data); testApi.get(function(err, result) { result.should.deep.equal(data); done(); }); this.requests[0].respond(200, { 'Content-Type': 'text/json' }, dataJson); });
咱們定義了一個對象data
和它的JSON版本dataJson
做爲咱們將要傳遞的數據。下一步,咱們調用了testApi.call
,在它的回掉函數中咱們使用了result.should.deep.equal
來驗證結果是否是與咱們所指望的數據一致。咱們還調用了done()
,它的做用是告訴 Mocha 該異步測試完成了。在這裏,要注意done
是測試函數中的一個參數。
最後,咱們調用了this.requests[0].respond
。你還記得以前的beforeEach
函數麼?它在每一次的測試開始以前,將全部的 fake XMLHttpRquest 放入了this.requests
中。當咱們的測試調用testApi.get
時,它建立了一個請求。這個請求被存入了this.requests
這個數組中,this.requests[0]
就表明了這個請求。
一般來講,XMLHttpRequest 沒有respond
函數,這裏的respond
用來響應一個 fake request。咱們在該響應中設置狀態碼爲200
,意思是成功響應。同時,咱們將響應頭中的Content-Type
設置爲text/json
,這是由於咱們傳遞的是 JSON 數據。respond
函數中的最後一個參數表示響應體,咱們將其設置爲以前建立好的dataJson
變量。
fake XMLHttpRequest 模擬了一個 GET 請求的響應,在獲得響應以後,testApi.get
中的回掉函數將會被調用。該回調函數中,咱們將響應中獲得的結果與data
變量作對比:
testApi.get(function(err, result) { result.should.deep.equal(data); done(); });
由於以前咱們建立了data
和dataJson
變量來表明傳遞的數據,因此若是響應正確,響應的數據應該被解析成一個對象。
如今,咱們能夠在瀏覽器中運行咱們的測試了,打開test.html
文件,你應該能夠看到關於測試經過的信息。
在咱們的示例中還有一個向服務器發送數據的post
函數,發送的數據是 JSON 格式的。接下來咱們就要完成對該函數的測試。
it('should send given data as JSON body', function() { var data = { hello: 'world' }; var dataJson = JSON.stringify(data); testApi.post(data, function() { }); this.requests[0].requestBody.should.equal(dataJson); });
就和以前同樣,咱們首先定義了一個測試數據data
和它的 JSON 格式的變量dataJson
。以後咱們調用了testApi.post
。與以前測試 GET 請求不同的地方是,這一次咱們只須要驗證要被髮送的數據是否被正確的轉換成了 JSON 格式,由於咱們只須要保證 POST 請求中發出的數據是正確的,所以咱們的回掉函數是空的。
該段代碼中最後一行使用了一個斷言來肯定發送數據的正確性。與以前同樣,咱們使用了 fake XMLHttpRequest,可是這一次咱們要證實它攜帶了正確的數據。POST 請求中攜帶的數據存放在 fake XMLHttpRequest 的 requestBody
屬性中,咱們將其與dataJson
做比較來驗證咱們的行爲。
做爲最後一個示例,讓咱們來是測試一個失敗的請求。由於網絡鏈接中可能出現不少問題,同時服務器也可能出現問題,而且咱們不該該讓咱們網站的用戶對具體的錯誤信息感到疑惑,因此錯誤測試很是重要。
示例模塊中的回調函數中有兩個參數,第一個參數就是錯誤信息,第二個參數則是每一次 Http 請求響應獲得的結果。代碼以下:
it('should return error into callback', function(done) { testApi.get(function(err, result) { err.should.exist; done(); }); this.requests[0].respond(500); });
因爲是錯誤測試,因此這一次咱們不須要任何數據。咱們調用了testApi.get
,並在其回掉函數中來驗證 error 參數的存在。爲了模擬響應錯誤,最後一行中的 fake XMLHttpRequest 發送了一個內部服務器錯誤的狀態碼 500 來觸發錯誤處理程序。
Ajax請求的測試是很重要的,若是你能證實每一次請求都是正確的,那麼你應用程序中的其餘部分就能徹底相信每一次 Ajax 請求獲得的數據。假如你正在使用 JQuery Ajax,測試的方法與例子的方法是如出一轍的。你一樣可使用 Sinon 中的 fake XMLHttpRequests。固然,Sinon 中並不僅有 fake XMLHttpRequest, 它還有 fake Server 用來模擬服務器響應,感興趣的話能夠去 Sinon 的官網瞭解。