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

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

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

Feb 18, 2025 am 10:13 AM

Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs

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

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

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

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

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

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

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

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

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

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

簡(jiǎn)而言之,Sinon 允許您將測(cè)試的困難部分替換為使測(cè)試變得簡(jiǎn)單的部分。

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

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

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

通過使用 Sinon,我們可以解決這兩個(gè)問題(以及許多其他問題),并消除復(fù)雜性。

Sinon 如何工作?

Sinon 通過允許您輕松創(chuàng)建所謂的 測(cè)試替身 來幫助消除測(cè)試中的復(fù)雜性。

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

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

Sinon 將測(cè)試替身分為三種類型:

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

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

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

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

安裝 Sinon

首先,我們需要安裝 Sinon。

對(duì)于 Node.js 測(cè)試:

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

對(duì)于基于瀏覽器的測(cè)試:

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

入門

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

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

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

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

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

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

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

間諜

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

間諜的主要用途是收集有關(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 返回一個(gè) Spy 對(duì)象,可以像函數(shù)一樣調(diào)用它,但還包含有關(guān)對(duì)其進(jìn)行的任何調(diào)用的信息的屬性。在上面的示例中,firstCall 屬性包含有關(guān)第一次調(diào)用的信息,例如 firstCall.args,它是傳遞的參數(shù)列表。

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

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

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

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

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

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

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

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

在實(shí)踐中,您可能不會(huì)經(jīng)常使用間諜。您更有可能需要一個(gè)存根,但間諜可能很方便,例如驗(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);
  });
});

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

Sinon 的斷言

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

在大多數(shù)使用間諜(和存根)的測(cè)試情況下,您需要某種方法來驗(yàn)證測(cè)試的結(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)是失敗時(shí)的錯(cuò)誤消息不清楚。您只會(huì)收到“false 不是 true”或類似的提示。正如您可能想象的那樣,這在找出問題所在方面幫助不大,您需要查看測(cè)試的源代碼才能弄清楚。不好玩。

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

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

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

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

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

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

但是,當(dāng)我們可以使用 Sinon 自身的斷言 時(shí),為什么要費(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 的斷言可以立即提供更好的錯(cuò)誤消息。當(dāng)您需要驗(yàn)證更復(fù)雜的條件(例如函數(shù)的參數(shù))時(shí),這會(huì)非常有用。

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

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

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

存根

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

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

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

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

assert(callback.calledOnce);

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

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

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

這是一個(gè)我們將測(cè)試的示例函數(shù)。它將對(duì)象作為參數(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,測(cè)試這將很困難,但如果我們使用存根,它就會(huì)變得很容易。

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

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

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

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

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

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

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

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

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

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

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

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

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

例如,對(duì)于我們的 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)建了一個(gè)存根,但這次我們沒有將其設(shè)置為 yield。此測(cè)試不關(guān)心回調(diào),因此讓它 yield 是不必要的。

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

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

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

模擬

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

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

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

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

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

var spy = sinon.spy();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sinon 不是魔術(shù)

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

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

首先,間諜本質(zhì)上是一個(gè)函數(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ù)獲得間諜功能。但是請(qǐng)注意,Sinon 的間諜提供了更廣泛的功能——包括斷言支持。這使得 Sinon 更方便。

那存根呢?

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

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

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

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

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

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

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

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

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

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

結(jié)論

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

如何使存根返回特定值?

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

如何使存根拋出錯(cuò)誤?

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

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

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

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機(jī)

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)頁(yè)開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)代碼編輯軟件(SublimeText3)

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

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

JavaScript評(píng)論:簡(jiǎn)短說明 JavaScript評(píng)論:簡(jiǎn)短說明 Jun 19, 2025 am 12:40 AM

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

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

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

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

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

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

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

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

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

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

See all articles