国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
關(guān)鍵要點(diǎn)
是什麼使 Sinon 如此重要和有用?
Sinon 如何工作?
安裝 Sinon
入門
間諜
Sinon 的斷言
存根
模擬
重要的最佳實(shí)踐:使用 sinon.test()
Sinon 不是魔術(shù)
那存根呢?
結(jié)論
關(guān)於 Sinon.js 測試的常見問題解答 (FAQ)
Sinon.js 中的模擬、間諜和存根有什麼區(qū)別?
如何在 JavaScript 中使用 Sinon.js 進(jìn)行單元測試?
如何在 Sinon.js 中創(chuàng)建間諜?
如何在 Sinon.js 中創(chuàng)建存根?
如何在 Sinon.js 中創(chuàng)建模擬?
如何將 Sinon.js 與其他測試框架一起使用?
如何將存根或間諜恢復(fù)到其原始函數(shù)?
如何檢查是否使用特定參數(shù)調(diào)用了間諜?
如何使存根返回特定值?
如何使存根拋出錯誤?
首頁 web前端 js教程 Sinon教程:使用模擬,間諜和存根的JavaScript測試

Sinon教程:使用模擬,間諜和存根的JavaScript測試

Feb 18, 2025 am 10:13 AM

Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs

本文由 Mark Brown 和 Marc Towler 審核。感謝所有 SitePoint 的同行評審員,使 SitePoint 的內(nèi)容達(dá)到最佳狀態(tài)!

編寫單元測試時,最大的障礙之一是如何處理非平凡的代碼。

在實(shí)際項(xiàng)目中,代碼經(jīng)常執(zhí)行各種使測試變得困難的操作。 Ajax 請求、計(jì)時器、日期、訪問其他瀏覽器功能……或者如果您使用的是 Node.js,數(shù)據(jù)庫總是很有趣,網(wǎng)絡(luò)或文件訪問也是如此。

所有這些都很難測試,因?yàn)槟鸁o法在代碼中控制它們。如果您使用的是 Ajax,則需要一個服務(wù)器來響應(yīng)請求,以便使您的測試通過。如果您使用 setTimeout,您的測試將不得不等待。對於數(shù)據(jù)庫或網(wǎng)絡(luò),情況也是一樣——您需要一個包含正確數(shù)據(jù)的數(shù)據(jù)庫,或一個網(wǎng)絡(luò)服務(wù)器。

現(xiàn)實(shí)生活不像許多測試教程看起來那樣容易。但您知道有解決方案嗎?

通過使用 Sinon,我們可以使測試非平凡的代碼變得微不足道!

讓我們看看它是如何工作的。

關(guān)鍵要點(diǎn)

  • Sinon 簡化測試:Sinon.js 對於簡化涉及復(fù)雜操作(如 Ajax 調(diào)用、計(jì)時器和數(shù)據(jù)庫交互)的 JavaScript 代碼的測試至關(guān)重要,因?yàn)樗试S用模擬、間諜和存根替換這些部分。
  • 三種類型的測試替身:Sinon 將測試替身分為間諜(收集有關(guān)函數(shù)調(diào)用的信息);存根(可以替換函數(shù)以強(qiáng)制執(zhí)行特定行為);以及模擬(非常適合替換和斷言整個對象的行為)。
  • 實(shí)用案例:Sinon 在單元測試場景中特別有用,在這些場景中,外部依賴項(xiàng)會使測試複雜化或減慢測試速度,例如外部 API 調(diào)用或基於時間的函數(shù)。
  • 集成和設(shè)置:Sinon 可以輕鬆集成到 Node.js 和基於瀏覽器的測試環(huán)境中,增強(qiáng)其在各種 JavaScript 應(yīng)用程序中的多功能性和易用性。
  • 增強(qiáng)的斷言:Sinon 提供增強(qiáng)的斷言方法,這些方法會生成更清晰的錯誤消息,從而改進(jìn)測試失敗期間的調(diào)試過程。
  • 最佳實(shí)踐:使用 sinon.test() 包裝測試用例可確保正確清理測試替身,防止在其他測試中產(chǎn)生副作用,並減少測試套件中潛在的錯誤。

是什麼使 Sinon 如此重要和有用?

簡而言之,Sinon 允許您將測試的困難部分替換為使測試變得簡單的部分。

在測試一段代碼時,您不希望它受到測試外部任何因素的影響。如果某些外部因素影響測試,則測試會變得更加複雜,並且可能會隨機(jī)失敗。

如果您想測試進(jìn)行 Ajax 調(diào)用的代碼,該如何操作?您需要運(yùn)行一個服務(wù)器並確保它提供測試所需的精確響應(yīng)。設(shè)置起來很複雜,並且使編寫和運(yùn)行單元測試變得困難。

如果您的代碼依賴於時間怎麼辦?假設(shè)它等待一秒鐘後再執(zhí)行某些操作?,F(xiàn)在怎麼辦?您可以在測試中使用 setTimeout 來等待一秒鐘,但這會使測試變慢。想像一下,如果間隔更長,例如五分鐘。我猜您可能不想每次運(yùn)行測試時都等待五分鐘。

通過使用 Sinon,我們可以解決這兩個問題(以及許多其他問題),並消除複雜性。

Sinon 如何工作?

Sinon 通過允許您輕鬆創(chuàng)建所謂的 測試替身 來幫助消除測試中的複雜性。

顧名思義,測試替身是測試中使用的代碼片段的替代品。回顧 Ajax 示例,我們不會設(shè)置服務(wù)器,而是會將 Ajax 調(diào)用替換為測試替身。對於時間示例,我們將使用測試替身來允許我們“向前移動時間”。

這聽起來可能有點(diǎn)奇怪,但基本概念很簡單。因?yàn)?JavaScript 非常動態(tài),所以我們可以獲取任何函數(shù)並將其替換為其他內(nèi)容。測試替身只是將這個想法更進(jìn)一步。使用 Sinon,我們可以用測試替身替換任何 JavaScript 函數(shù),然後可以對其進(jìn)行配置以執(zhí)行各種操作,使測試複雜的事情變得簡單。

Sinon 將測試替身分為三種類型:

  • 間諜,提供有關(guān)函數(shù)調(diào)用的信息,而不會影響其行為
  • 存根,就像間諜一樣,但完全替換了函數(shù)。這使得可以使存根函數(shù)執(zhí)行任何您喜歡的事情——拋出異常、返回特定值等
  • 模擬,通過組合間諜和存根,使替換整個對象更容易

此外,Sinon 還提供了一些其他幫助程序,儘管這些幫助程序不在本文的討論範(fàn)圍之內(nèi):

  • 假計(jì)時器,可用於向前移動時間,例如觸發(fā) setTimeout
  • 假 XMLHttpRequest 和服務(wù)器,可用於偽造 Ajax 請求和響應(yīng)

憑藉這些功能,Sinon 允許您解決外部依賴項(xiàng)在測試中導(dǎo)致的所有難題。如果您學(xué)習(xí)有效使用 Sinon 的技巧,則不需要任何其他工具。

安裝 Sinon

首先,我們需要安裝 Sinon。

對於 Node.js 測試:

  1. 使用 npm install sinon 通過 npm 安裝 Sinon
  2. 使用 var sinon = require('sinon'); 在您的測試中引入 Sinon

對於基於瀏覽器的測試:

  1. 您可以使用 npm install sinon 通過 npm 安裝 Sinon,使用 CDN 或從 Sinon 的網(wǎng)站下載它
  2. 在您的測試運(yùn)行程序頁面中包含 sinon.js。

入門

Sinon 具有許多功能,但其中許多功能都是建立在其自身之上的。您了解一部分,就已經(jīng)了解了下一部分。一旦您了解了基礎(chǔ)知識並了解了每個不同部分的作用,這就會使 Sinon 易於使用。

當(dāng)我們的代碼調(diào)用給我們帶來麻煩的函數(shù)時,我們通常需要 Sinon。

對於 Ajax,它可能是 $.get 或 XMLHttpRequest。對於時間,該函數(shù)可能是 setTimeout。對於數(shù)據(jù)庫,它可能是 mongodb.findOne。

為了更容易討論此函數(shù),我將其稱為 依賴項(xiàng)。我們正在測試的函數(shù) 依賴 於另一個函數(shù)的結(jié)果。

我們可以說,Sinon 的基本使用模式是用測試替身替換有問題的依賴項(xiàng)。

  • 在測試 Ajax 時,我們將 XMLHttpRequest 替換為一個模擬 Ajax 請求的測試替身
  • 在測試時間時,我們將 setTimeout 替換為一個假計(jì)時器
  • 在測試數(shù)據(jù)庫訪問時,我們可以將 mongodb.findOne 替換為一個立即返回一些假數(shù)據(jù)的測試替身

讓我們看看它在實(shí)踐中是如何工作的。

間諜

間諜是 Sinon 最簡單的部分,其他功能建立在其之上。

間諜的主要用途是收集有關(guān)函數(shù)調(diào)用的信息。您還可以使用它們來幫助驗(yàn)證某些事情,例如是否調(diào)用了函數(shù)。

var spy = sinon.spy();

//我們可以像函數(shù)一樣調(diào)用間諜
spy('Hello', 'World');

//現(xiàn)在我們可以獲取有關(guān)調(diào)用的信息
console.log(spy.firstCall.args); //輸出:['Hello', 'World']

函數(shù) sinon.spy 返回一個 Spy 對象,可以像函數(shù)一樣調(diào)用它,但還包含有關(guān)對其進(jìn)行的任何調(diào)用的信息的屬性。在上面的示例中,firstCall 屬性包含有關(guān)第一次調(diào)用的信息,例如 firstCall.args,它是傳遞的參數(shù)列表。

儘管您可以通過不帶參數(shù)地調(diào)用 sinon.spy 來創(chuàng)建匿名間諜,但更常見的模式是用間諜替換另一個函數(shù)。

var user = {
  ...
  setName: function(name){
    this.name = name;
  }
}

//為 setName 函數(shù)創(chuàng)建一個間諜
var setNameSpy = sinon.spy(user, 'setName');

//現(xiàn)在,每當(dāng)我們調(diào)用該函數(shù)時,間諜都會記錄有關(guān)它的信息
user.setName('Darth Vader');

//我們可以通過查看間諜對象來查看
console.log(setNameSpy.callCount); //輸出:1

//重要最后一步 - 刪除間諜
setNameSpy.restore();

用間諜替換另一個函數(shù)的工作方式與前面的示例類似,但有一個重要的區(qū)別:完成使用間諜後,務(wù)必記住恢復(fù)原始函數(shù),如上面示例的最後一行所示。如果沒有這個,您的測試可能會行為異常。

間諜有很多不同的屬性,這些屬性提供了關(guān)於它們?nèi)绾问褂玫牟煌畔ⅰ?Sinon 的間諜文檔包含所有可用選項(xiàng)的完整列表。

在實(shí)踐中,您可能不會經(jīng)常使用間諜。您更有可能需要一個存根,但間諜可能很方便,例如驗(yàn)證是否調(diào)用了回調(diào):

function myFunction(condition, callback){
  if(condition){
    callback();
  }
}

describe('myFunction', function() {
  it('should call the callback function', function() {
    var callback = sinon.spy();

    myFunction(true, callback);

    assert(callback.calledOnce);
  });
});

在這個例子中,我使用 Mocha 作為測試框架,使用 Chai 作為斷言庫。如果您想了解更多關(guān)於這兩個的信息,請參考我之前的文章:使用 Mocha 和 Chai 對您的 JavaScript 進(jìn)行單元測試。

Sinon 的斷言

在我們繼續(xù)討論存根之前,讓我們快速繞道一下,看看 Sinon 的斷言。

在大多數(shù)使用間諜(和存根)的測試情況下,您需要某種方法來驗(yàn)證測試的結(jié)果。

我們可以使用任何類型的斷言來驗(yàn)證結(jié)果。在前面關(guān)於回調(diào)的示例中,我們使用了 Chai 的 assert 函數(shù),它確保該值是真值。

var spy = sinon.spy();

//我們可以像函數(shù)一樣調(diào)用間諜
spy('Hello', 'World');

//現(xiàn)在我們可以獲取有關(guān)調(diào)用的信息
console.log(spy.firstCall.args); //輸出:['Hello', 'World']

這樣做的缺點(diǎn)是失敗時的錯誤消息不清楚。您只會收到“false 不是 true”或類似的提示。正如您可能想像的那樣,這在找出問題所在方面幫助不大,您需要查看測試的源代碼才能弄清楚。不好玩。

為了解決這個問題,我們可以將自定義錯誤消息包含到斷言中。

var user = {
  ...
  setName: function(name){
    this.name = name;
  }
}

//為 setName 函數(shù)創(chuàng)建一個間諜
var setNameSpy = sinon.spy(user, 'setName');

//現(xiàn)在,每當(dāng)我們調(diào)用該函數(shù)時,間諜都會記錄有關(guān)它的信息
user.setName('Darth Vader');

//我們可以通過查看間諜對象來查看
console.log(setNameSpy.callCount); //輸出:1

//重要最后一步 - 刪除間諜
setNameSpy.restore();

但是,當(dāng)我們可以使用 Sinon 自身的斷言 時,為什麼要費(fèi)心呢?

function myFunction(condition, callback){
  if(condition){
    callback();
  }
}

describe('myFunction', function() {
  it('should call the callback function', function() {
    var callback = sinon.spy();

    myFunction(true, callback);

    assert(callback.calledOnce);
  });
});

像這樣使用 Sinon 的斷言可以立即提供更好的錯誤消息。當(dāng)您需要驗(yàn)證更複雜的條件(例如函數(shù)的參數(shù))時,這會非常有用。

以下是 Sinon 提供的其他一些有用斷言的示例:

  • sinon.assert.calledWith 可用於驗(yàn)證是否使用特定參數(shù)調(diào)用了函數(shù)(這可能是我最常用的一個)
  • sinon.assert.callOrder 可以驗(yàn)證函數(shù)是否按特定順序調(diào)用

與間諜一樣,Sinon 的斷言文檔包含所有可用的選項(xiàng)。如果您喜歡使用 Chai,還有一個 sinon-chai 插件可用,它允許您通過 Chai 的 expect 或 should 接口使用 Sinon 斷言。

存根

存根是首選的測試替身,因?yàn)樗鼈冹`活且方便。它們具有間諜的所有功能,但它們不僅僅是監(jiān)視函數(shù)的作用,存根完全替換了它。換句話說,使用間諜時,原始函數(shù)仍然運(yùn)行,但使用存根時,它不會運(yùn)行。

這使得存根非常適合許多任務(wù),例如:

  • 替換使測試編寫緩慢且困難的 Ajax 或其他外部調(diào)用
  • 根據(jù)函數(shù)輸出觸發(fā)不同的代碼路徑
  • 測試異常情況,例如拋出異常時會發(fā)生什麼?

我們可以創(chuàng)建存根的方式與間諜類似……

assert(callback.calledOnce);

我們可以像間諜一樣創(chuàng)建匿名存根,但是當(dāng)您使用存根替換現(xiàn)有函數(shù)時,存根會變得非常有用。

例如,如果我們有一些使用 jQuery 的 Ajax 功能的代碼,則測試它很困難。代碼會向我們配置的任何服務(wù)器發(fā)送請求,因此我們需要使它可用,或者向代碼添加一個特殊情況,以便不在測試環(huán)境中執(zhí)行此操作——這是一個很大的禁忌。您幾乎不應(yīng)該在代碼中包含特定於測試的情況。

與其求助於不良做法,我們可以使用 Sinon 並將 Ajax 功能替換為存根。這使得測試它變得微不足道。

這是一個我們將測試的示例函數(shù)。它將對像作為參數(shù),並通過 Ajax 將其發(fā)送到預(yù)定義的 URL。

var spy = sinon.spy();

//我們可以像函數(shù)一樣調(diào)用間諜
spy('Hello', 'World');

//現(xiàn)在我們可以獲取有關(guān)調(diào)用的信息
console.log(spy.firstCall.args); //輸出:['Hello', 'World']

通常,由於 Ajax 調(diào)用和預(yù)定義的 URL,測試這將很困難,但如果我們使用存根,它就會變得很容易。

假設(shè)我們要確保傳遞給 saveUser 的回調(diào)函數(shù)在請求完成後正確調(diào)用。

var user = {
  ...
  setName: function(name){
    this.name = name;
  }
}

//為 setName 函數(shù)創(chuàng)建一個間諜
var setNameSpy = sinon.spy(user, 'setName');

//現(xiàn)在,每當(dāng)我們調(diào)用該函數(shù)時,間諜都會記錄有關(guān)它的信息
user.setName('Darth Vader');

//我們可以通過查看間諜對象來查看
console.log(setNameSpy.callCount); //輸出:1

//重要最后一步 - 刪除間諜
setNameSpy.restore();

在這裡,我們用存根替換 Ajax 函數(shù)。這意味著請求永遠(yuǎn)不會發(fā)送,我們不需要服務(wù)器或任何東西——我們完全控制測試代碼中發(fā)生的事情!

因?yàn)槲覀兿氪_保我們傳遞給 saveUser 的回調(diào)被調(diào)用,所以我們將指示存根 yield。這意味著存根會自動調(diào)用作為參數(shù)傳遞給它的第一個函數(shù)。這模擬了 $.post 的行為,它會在請求完成後調(diào)用回調(diào)。

除了存根之外,我們還在此測試中創(chuàng)建了一個間諜。我們可以使用普通函數(shù)作為回調(diào),但使用間諜可以很容易地使用 Sinon 的 sinon.assert.calledOnce 斷言來驗(yàn)證測試的結(jié)果。

在大多數(shù)情況下,當(dāng)您需要存根時,您可以遵循相同的基本模式:

  • 找到有問題的函數(shù),例如 $.post
  • 查看它的工作方式,以便您可以在測試中模擬它
  • 創(chuàng)建一個存根
  • 將存根設(shè)置為在測試中具有所需的行為

存根不需要模擬每種行為。測試所需的唯一行為是必要的,其他任何東西都可以省略。

存根的另一個常見用法是驗(yàn)證是否使用特定參數(shù)集調(diào)用了函數(shù)。

例如,對於我們的 Ajax 功能,我們要確保發(fā)送正確的值。因此,我們可以有類似以下內(nèi)容:

function myFunction(condition, callback){
  if(condition){
    callback();
  }
}

describe('myFunction', function() {
  it('should call the callback function', function() {
    var callback = sinon.spy();

    myFunction(true, callback);

    assert(callback.calledOnce);
  });
});

同樣,我們?yōu)?$.post() 創(chuàng)建了一個存根,但這次我們沒有將其設(shè)置為 yield。此測試不關(guān)心回調(diào),因此讓它 yield 是不必要的。

我們設(shè)置了一些變量來包含預(yù)期數(shù)據(jù)——URL 和參數(shù)。設(shè)置這樣的變量是一個好習(xí)慣,因?yàn)樗梢宰屛覀円荒苛巳坏乜吹綔y試的要求。它還可以幫助我們設(shè)置用戶變量而無需重複值。

這次我們使用了 sinon.assert.calledWith() 斷言。我們將存根作為它的第一個參數(shù)傳遞,因?yàn)檫@次我們要驗(yàn)證存根是否使用正確的參數(shù)調(diào)用。

還有另一種在 Sinon 中測試 Ajax 請求的方法。這是通過使用 Sinon 的假 XMLHttpRequest 功能來實(shí)現(xiàn)的。我們不會在這裡詳細(xì)介紹它,但如果您想了解它的工作原理,請參閱我關(guān)於使用 Sinon 的假 XMLHttpRequest 進(jìn)行 Ajax 測試的文章。

模擬

模擬是一種與存根不同的方法。如果您聽說過“模擬對象”這個術(shù)語,這就是同樣的意思——Sinon 的模擬可以用來替換整個對象並改變它們的行為,類似於存根函數(shù)。

如果您需要從單個對象存根多個函數(shù),它們主要是有用的。如果您只需要替換單個函數(shù),則存根更容易使用。

使用模擬時,您應(yīng)該小心!由於它們的強(qiáng)大功能,很容易使您的測試過於具體——測試過多且過於具體的事情——這可能會使您的測試無意中變得脆弱。

與間諜和存根不同,模擬具有內(nèi)置斷言。您可以預(yù)先定義預(yù)期結(jié)果,方法是告訴模擬對象需要發(fā)生什麼,然後在測試結(jié)束時調(diào)用驗(yàn)證函數(shù)。

假設(shè)我們使用 store.js 將內(nèi)容保存到 localStorage,並且我們想測試與之相關(guān)的函數(shù)。我們可以使用模擬來幫助測試它,如下所示:

var spy = sinon.spy();

//我們可以像函數(shù)一樣調(diào)用間諜
spy('Hello', 'World');

//現(xiàn)在我們可以獲取有關(guān)調(diào)用的信息
console.log(spy.firstCall.args); //輸出:['Hello', 'World']

使用模擬時,我們使用流暢的調(diào)用樣式定義預(yù)期的調(diào)用及其結(jié)果,如上所示。這與使用斷言驗(yàn)證測試結(jié)果相同,只是我們預(yù)先定義了它們,並且為了驗(yàn)證它們,我們在測試結(jié)束時調(diào)用 storeMock.verify()。

在 Sinon 的模擬對象術(shù)語中,調(diào)用 mock.expects('something') 會創(chuàng)建一個 期望。也就是說,方法 mock.something() 預(yù)計(jì)會被調(diào)用。每個期望除了模擬特定功能外,還支持與間諜和存根相同的函數(shù)。

您可能會發(fā)現(xiàn),使用存根通常比使用模擬更容易——這完全沒問題。應(yīng)該謹(jǐn)慎使用模擬。

有關(guān)模擬特定函數(shù)的完整列表,請查看 Sinon 的模擬文檔。

重要的最佳實(shí)踐:使用 sinon.test()

Sinon 有一個重要的最佳實(shí)踐,在使用間諜、存根或模擬時應(yīng)記住。

如果您用測試替身替換現(xiàn)有函數(shù),請使用 sinon.test()。

在前面的示例中,我們使用 stub.restore() 或 mock.restore() 來清理使用它們後的內(nèi)容。這是必要的,因?yàn)榉駝t測試替身將保留在原處,並且可能會對其他測試產(chǎn)生負(fù)面影響或?qū)е洛e誤。

但是直接使用 restore() 函數(shù)是有問題的。被測試的函數(shù)可能會導(dǎo)致錯誤,並在調(diào)用 restore() 之前結(jié)束測試函數(shù)!

我們有兩種方法可以解決這個問題:我們可以將整個內(nèi)容包裝在一個 try catch 塊中。這允許我們將 restore() 調(diào)用放在 finally 塊中,確保無論發(fā)生什麼都會運(yùn)行它。

或者,更好的方法是使用 sinon.test() 包裝測試函數(shù)

var user = {
  ...
  setName: function(name){
    this.name = name;
  }
}

//為 setName 函數(shù)創(chuàng)建一個間諜
var setNameSpy = sinon.spy(user, 'setName');

//現(xiàn)在,每當(dāng)我們調(diào)用該函數(shù)時,間諜都會記錄有關(guān)它的信息
user.setName('Darth Vader');

//我們可以通過查看間諜對象來查看
console.log(setNameSpy.callCount); //輸出:1

//重要最后一步 - 刪除間諜
setNameSpy.restore();

在上面的示例中,請注意 it() 的第二個參數(shù)包裝在 sinon.test() 中。需要注意的第二件事是,我們使用 this.stub() 而不是 sinon.stub()。

使用sinon.test() 包裝測試允許我們使用Sinon 的沙箱 功能,允許我們通過this.spy()、this.stub() 和this.mock() 創(chuàng)建間諜、存根和模擬。使用沙箱創(chuàng)建的任何測試替身都會 自動 清理。

請注意,我們上面的示例代碼沒有 stub.restore()——由於測試被沙箱化,因此它是不必要的。

如果盡可能使用 sinon.test(),您可以避免由於早期測試由於錯誤而沒有清理其測試替身而導(dǎo)致測試開始隨機(jī)失敗的問題。

Sinon 不是魔術(shù)

Sinon 執(zhí)行許多操作,有時可能很難理解它的工作原理。讓我們來看看 Sinon 工作原理的一些簡單的 JavaScript 示例,以便我們可以更好地了解它的內(nèi)部工作原理。這將幫助您在不同的情況下更有效地使用它。

我們也可以手動創(chuàng)建間諜、存根和模擬。我們使用 Sinon 的原因是它使任務(wù)變得微不足道——手動創(chuàng)建它們可能非常複雜,但讓我們看看它是如何工作的,以了解 Sinon 的作用。

首先,間諜本質(zhì)上是一個函數(shù)包裝器:

var spy = sinon.spy();

//我們可以像函數(shù)一樣調(diào)用間諜
spy('Hello', 'World');

//現(xiàn)在我們可以獲取有關(guān)調(diào)用的信息
console.log(spy.firstCall.args); //輸出:['Hello', 'World']

我們可以很容易地使用自定義函數(shù)獲得間諜功能。但是請注意,Sinon 的間諜提供了更廣泛的功能——包括斷言支持。這使得 Sinon 更方便。

那存根呢?

要創(chuàng)建一個非常簡單的存根,您可以簡單地用一個新函數(shù)替換一個函數(shù):

var user = {
  ...
  setName: function(name){
    this.name = name;
  }
}

//為 setName 函數(shù)創(chuàng)建一個間諜
var setNameSpy = sinon.spy(user, 'setName');

//現(xiàn)在,每當(dāng)我們調(diào)用該函數(shù)時,間諜都會記錄有關(guān)它的信息
user.setName('Darth Vader');

//我們可以通過查看間諜對象來查看
console.log(setNameSpy.callCount); //輸出:1

//重要最后一步 - 刪除間諜
setNameSpy.restore();

但是,Sinon 的存根提供了幾個優(yōu)點(diǎn):

  • 它們包含完整的間諜功能
  • 您可以使用 stub.restore() 輕鬆恢復(fù)原始行為
  • 您可以針對 Sinon 存根進(jìn)行斷言

模擬只是結(jié)合了間諜和存根的行為,從而可以以不同的方式使用它們的功能。

即使 Sinon 有時看起來像是做了很多“魔術(shù)”,在大多數(shù)情況下,這也可以很容易地用您自己的代碼來完成。 Sinon 使用起來要方便得多,而不是必須為該目的編寫自己的庫。

結(jié)論

測試現(xiàn)實(shí)生活中的代碼有時似乎過於復(fù)雜,很容易完全放棄。但是藉助 Sinon,測試幾乎任何類型的代碼都變得輕而易舉。

只需記住主要原則:如果函數(shù)使您的測試難以編寫,請嘗試將其替換為測試替身。無論函數(shù)執(zhí)行什麼操作,此原則都適用。

想要了解如何在您自己的代碼中應(yīng)用 Sinon?訪問我的網(wǎng)站,我會向您發(fā)送我的免費(fèi)現(xiàn)實(shí)世界中的 Sinon 指南,其中包括 Sinon 最佳實(shí)踐以及如何在不同類型的測試情況下應(yīng)用它的三個現(xiàn)實(shí)世界示例!

關(guān)於 Sinon.js 測試的常見問題解答 (FAQ)

Sinon.js 中的模擬、間諜和存根有什麼區(qū)別?

在 Sinon.js 中,模擬、間諜和存根具有不同的用途。間諜是記錄所有調(diào)用的參數(shù)、返回值、this 的值和拋出的異常(如果有)的函數(shù)。它們可用於跟蹤函數(shù)調(diào)用和響應(yīng)。存根類似於間諜,但具有預(yù)編程行為。它們還會記錄有關(guān)如何調(diào)用它們的信息,但與間諜不同,它們可用於控制方法的行為,以強(qiáng)制方法拋出錯誤或返回特定值。模擬是具有預(yù)編程行為(如存根)以及預(yù)編程期望的假方法(如間諜)。

如何在 JavaScript 中使用 Sinon.js 進(jìn)行單元測試?

Sinon.js 是一個強(qiáng)大的工具,用於在 JavaScript 測試中創(chuàng)建間諜、存根和模擬。要使用它,您首先需要將其包含在您的項(xiàng)目中,方法是在您的 HTML 中使用腳本標(biāo)記或通過 npm 安裝它。包含後,您可以使用其 API 創(chuàng)建和管理間諜、存根和模擬。然後,這些可以用於隔離您正在測試的代碼並確保它按預(yù)期運(yùn)行。

如何在 Sinon.js 中創(chuàng)建間諜?

在 Sinon.js 中創(chuàng)建間諜很簡單。您只需調(diào)用 sinon.spy() 函數(shù)。這將返回一個間諜函數(shù),您可以在測試中使用它。間諜將記錄有關(guān)如何調(diào)用它的信息,然後您可以在測試中檢查這些信息。例如,您可以檢查調(diào)用間諜的次數(shù)、使用什麼參數(shù)調(diào)用它以及它返回的內(nèi)容。

如何在 Sinon.js 中創(chuàng)建存根?

要在 Sinon.js 中創(chuàng)建存根,您需要調(diào)用 sinon.stub() 函數(shù)。這將返回一個存根函數(shù),您可以在測試中使用它。存根的行為類似於間諜,記錄有關(guān)如何調(diào)用它的信息,但它還允許您控制其行為。例如,您可以使存根拋出錯誤或返回特定值。

如何在 Sinon.js 中創(chuàng)建模擬?

在 Sinon.js 中創(chuàng)建模擬涉及調(diào)用 sinon.mock() 函數(shù)。這將返回一個模擬對象,您可以在測試中使用它。模擬對象的行為類似於間諜,記錄有關(guān)如何調(diào)用它的信息,並且類似於存根,允許您控制其行為。但它還允許您設(shè)置有關(guān)如何調(diào)用它的期望。

如何將 Sinon.js 與其他測試框架一起使用?

Sinon.js 旨在與任何 JavaScript 測試框架一起使用。它提供了一個獨(dú)立的測試框架,但它也可以與 Mocha、Jasmine 和 QUnit 等其他流行的測試框架集成。 Sinon.js 文檔提供了與這些框架和其他測試框架集成的示例。

如何將存根或間諜恢復(fù)到其原始函數(shù)?

如果您已用存根或間諜替換了函數(shù),則可以通過調(diào)用存根或間諜上的 .restore() 方法來恢復(fù)原始函數(shù)。如果您想在測試後進(jìn)行清理,以確保存根或間諜不會影響其他測試,這將非常有用。

如何檢查是否使用特定參數(shù)調(diào)用了間諜?

Sinon.js 提供了幾種方法來檢查如何調(diào)用間諜。例如,您可以使用 .calledWith() 方法來檢查是否使用特定參數(shù)調(diào)用了間諜。您還可以使用 .calledOnceWith() 方法來檢查間諜是否只使用特定參數(shù)調(diào)用了一次。

如何使存根返回特定值?

您可以使用 .returns() 方法使存根返回特定值。例如,如果您有一個名為 myStub 的存根,則可以通過調(diào)用 myStub.returns('foo') 使其返回值 'foo'。

如何使存根拋出錯誤?

您可以使用 .throws() 方法使存根拋出錯誤。例如,如果您有一個名為 myStub 的存根,則可以通過調(diào)用 myStub.throws() 使其拋出錯誤。默認(rèn)情況下,這將拋出一個 Error 對象,但您也可以通過將錯誤的名稱作為參數(shù)傳遞來使其拋出特定類型的錯誤。

以上是Sinon教程:使用模擬,間諜和存根的JavaScript測試的詳細(xì)內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java vs. JavaScript:清除混亂 Java vs. JavaScript:清除混亂 Jun 20, 2025 am 12:27 AM

Java和JavaScript是不同的編程語言,各自適用於不同的應(yīng)用場景。 Java用於大型企業(yè)和移動應(yīng)用開發(fā),而JavaScript主要用於網(wǎng)頁開發(fā)。

JavaScript評論:簡短說明 JavaScript評論:簡短說明 Jun 19, 2025 am 12:40 AM

JavascriptconcommentsenceenceEncorenceEnterential gransimenting,reading and guidingCodeeXecution.1)單inecommentsareusedforquickexplanations.2)多l(xiāng)inecommentsexplaincomplexlogicorprovideDocumentation.3)

如何在JS中與日期和時間合作? 如何在JS中與日期和時間合作? Jul 01, 2025 am 01:27 AM

JavaScript中的日期和時間處理需注意以下幾點(diǎn):1.創(chuàng)建Date對像有多種方式,推薦使用ISO格式字符串以保證兼容性;2.獲取和設(shè)置時間信息可用get和set方法,注意月份從0開始;3.手動格式化日期需拼接字符串,也可使用第三方庫;4.處理時區(qū)問題建議使用支持時區(qū)的庫,如Luxon。掌握這些要點(diǎn)能有效避免常見錯誤。

JavaScript與Java:開發(fā)人員的全面比較 JavaScript與Java:開發(fā)人員的全面比較 Jun 20, 2025 am 12:21 AM

JavaScriptIspreferredforredforwebdevelverment,而Javaisbetterforlarge-ScalebackendsystystemsandSandAndRoidApps.1)JavascriptexcelcelsincreatingInteractiveWebexperienceswebexperienceswithitswithitsdynamicnnamicnnamicnnamicnnamicnemicnemicnemicnemicnemicnemicnemicnemicnddommanipulation.2)

為什麼要將標(biāo)籤放在的底部? 為什麼要將標(biāo)籤放在的底部? Jul 02, 2025 am 01:22 AM

PlacingtagsatthebottomofablogpostorwebpageservespracticalpurposesforSEO,userexperience,anddesign.1.IthelpswithSEObyallowingsearchenginestoaccesskeyword-relevanttagswithoutclutteringthemaincontent.2.Itimprovesuserexperiencebykeepingthefocusonthearticl

JavaScript:探索用於高效編碼的數(shù)據(jù)類型 JavaScript:探索用於高效編碼的數(shù)據(jù)類型 Jun 20, 2025 am 12:46 AM

javascripthassevenfundaMentalDatatypes:數(shù)字,弦,布爾值,未定義,null,object和symbol.1)numberSeadUble-eaduble-ecisionFormat,forwidevaluerangesbutbecautious.2)

什麼是在DOM中冒泡和捕獲的事件? 什麼是在DOM中冒泡和捕獲的事件? Jul 02, 2025 am 01:19 AM

事件捕獲和冒泡是DOM中事件傳播的兩個階段,捕獲是從頂層向下到目標(biāo)元素,冒泡是從目標(biāo)元素向上傳播到頂層。 1.事件捕獲通過addEventListener的useCapture參數(shù)設(shè)為true實(shí)現(xiàn);2.事件冒泡是默認(rèn)行為,useCapture設(shè)為false或省略;3.可使用event.stopPropagation()阻止事件傳播;4.冒泡支持事件委託,提高動態(tài)內(nèi)容處理效率;5.捕獲可用於提前攔截事件,如日誌記錄或錯誤處理。了解這兩個階段有助於精確控制JavaScript響應(yīng)用戶操作的時機(jī)和方式。

Java和JavaScript有什麼區(qū)別? Java和JavaScript有什麼區(qū)別? Jun 17, 2025 am 09:17 AM

Java和JavaScript是不同的編程語言。 1.Java是靜態(tài)類型、編譯型語言,適用於企業(yè)應(yīng)用和大型系統(tǒng)。 2.JavaScript是動態(tài)類型、解釋型語言,主要用於網(wǎng)頁交互和前端開發(fā)。

See all articles