目錄
模擬服務
模擬提供程序
模擬模塊
模擬返回 Promise 的方法
模擬全局對象
在 AngularJS 測試中模擬依賴項的目的是什麼?
如何在 AngularJS 中創建一個模擬服務?
首頁 web前端 js教程 AngularJS測試中的模擬依賴項

AngularJS測試中的模擬依賴項

Feb 20, 2025 pm 12:28 PM

Mocking Dependencies in AngularJS Tests

核心要點

  • AngularJS 天生就考慮到了測試,其內置的依賴注入機制使得每個組件都可使用任何 JavaScript 測試框架(如 Jasmine)進行測試。
  • 單元測試中的模擬涉及隔離測試代碼片段的功能,這可能具有挑戰性,因為依賴項來自不同的來源。 AngularJS 中的模擬通過 angular-mocks 模塊簡化了,該模塊為一組常用的 AngularJS 服務提供了模擬。
  • AngularJS 中的服務模擬可以通過獲取實際服務的實例並偵聽服務的方法,或者使用 $provide 實現模擬服務來完成。後者方法更可取,可以避免調用服務的實際方法實現。
  • AngularJS 中的提供程序模擬遵循與服務模擬類似的規則。測試中必須實現 $get 方法。如果測試文件中不需要 $get 函數中定義的功能,則可以將其賦值為空函數。
  • 全局對象(例如全局“window”對象的一部分或由第三方庫創建的對象)可以通過注入它們到 $window 或使用全局對象創建值或常量並根據需要注入它們來實現模擬。

AngularJS 的設計理念中就包含了測試。框架的源代碼經過了非常充分的測試,並且使用該框架編寫的任何代碼也都是可測試的。內置的依賴注入機制使得用 AngularJS 編寫的每個組件都可進行測試。 AngularJS 應用程序中的代碼可以使用任何現有的 JavaScript 測試框架進行單元測試。最常用於測試 AngularJS 代碼的框架是 Jasmine。本文中的所有示例代碼片段都是使用 Jasmine 編寫的。如果您在 Angular 項目中使用任何其他測試框架,您仍然可以應用本文中討論的思想。

本文假設您已經具備單元測試和測試 AngularJS 代碼的經驗。您不必是測試專家。如果您對測試有基本的了解,並且可以為 AngularJS 應用程序編寫一些簡單的測試用例,那麼您可以繼續閱讀本文。

模擬在單元測試中的作用

每個單元測試的任務都是隔離地測試一段代碼的功能。隔離被測系統有時可能具有挑戰性,因為依賴項可能來自不同的來源,我們需要充分理解要模擬的對象的職責。

在 JavaScript 等非靜態類型語言中,模擬很困難,因為不容易理解要模擬的對象的結構。同時,它也提供了靈活性,即僅模擬被測系統當前正在使用的對象的某一部分,而忽略其餘部分。

AngularJS 測試中的模擬

由於 AngularJS 的主要目標之一是可測試性,核心團隊為此付出了額外的努力,使測試更容易,並在 angular-mocks 模塊中為我們提供了一組模擬。此模塊包含圍繞一組 AngularJS 服務(例如 $http$timeout$animate 等)的模擬,這些服務廣泛用於任何 AngularJS 應用程序中。此模塊減少了開發人員編寫測試所需的大量時間。

在為真實的業務應用程序編寫測試時,這些模擬非常有幫助。同時,它們不足以測試整個應用程序。我們需要模擬框架中但未被模擬的任何依賴項——來自第三方插件的依賴項、全局對像或在應用程序中創建的依賴項。本文將介紹一些關於模擬 AngularJS 依賴項的技巧。

模擬服務

服務是 AngularJS 應用程序中最常見的依賴項類型。您可能已經知道,服務在 AngularJS 中是一個重載的術語。它可能指服務、工廠、值、常量或提供程序。我們將在下一節討論提供程序。服務可以通過以下方式之一進行模擬:

  • 使用注入塊獲取實際服務的實例並偵聽服務的方法。
  • 使用 $provide 實現模擬服務。

我不喜歡第一種方法,因為它可能導致調用服務的實際方法實現。我們將使用第二種方法來模擬以下服務:

angular.module('sampleServices', [])
  .service('util', function() {
    this.isNumber = function(num) {
      return !isNaN(num);
    };

    this.isDate = function(date) {
      return (date instanceof Date);
    };
  });
登入後複製
登入後複製
登入後複製

以下代碼片段創建了上述服務的模擬:

module(function($provide) {
  $provide.service('util', function() {
    this.isNumber = jasmine.createSpy('isNumber').andCallFake(function(num) {
      // 模拟实现
    });
    this.isDate = jasmine.createSpy('isDate').andCallFake(function(num) {
      // 模拟实现
    });
  });
});

// 获取模拟服务的引用
var mockUtilSvc;

inject(function(util) {
  mockUtilSvc = util;
});
登入後複製
登入後複製
登入後複製

儘管上面的示例使用 Jasmine 創建間諜,但您可以使用 Sinon.js 替換它,實現等效的功能。

最好在加載測試所需的所有模塊後創建所有模擬。否則,如果在一個已加載的模塊中定義了一個服務,則實際實現會覆蓋模擬實現。

常量、工廠和值可以使用 $provide.constant$provide.factory$provide.value 分別進行模擬。

模擬提供程序

模擬提供程序類似於模擬服務。編寫提供程序時必須遵循的所有規則也必須在模擬它們時遵循。考慮以下提供程序:

angular.module('mockingProviders',[])
  .provider('sample', function() {
    var registeredVals = [];

    this.register = function(val) {
      registeredVals.push(val);      
    };

    this.$get = function() {
      function getRegisteredVals() {
        return registeredVals;
      }

      return {
        getRegisteredVals: getRegisteredVals
      };
    };
  });
登入後複製
登入後複製
登入後複製

以下代碼片段為上述提供程序創建了一個模擬:

module(function($provide) {
  $provide.provider('sample', function() {
    this.register = jasmine.createSpy('register');

    this.$get = function() {
      var getRegisteredVals = jasmine.createSpy('getRegisteredVals');

      return {
        getRegisteredVals: getRegisteredVals
      };
    };
  });
});

// 获取提供程序的引用
var sampleProviderObj;

module(function(sampleProvider) {
  sampleProviderObj = sampleProvider;
});
登入後複製
登入後複製
登入後複製

獲取提供程序和其他單例的引用的區別在於,提供程序在此時不會在 inject() 塊中可用,因為提供程序此時已轉換為工廠。我們可以使用 module() 塊獲取它們的對象。

在定義提供程序的情況下,測試中也必須實現 $get 方法。如果您在測試文件中不需要 $get 函數中定義的功能,則可以將其賦值為空函數。

模擬模塊

如果要在測試文件中加載的模塊需要一堆其他模塊,則除非加載所有必需的模塊,否則無法加載被測模塊。加載所有這些模塊有時會導致測試失敗,因為某些實際的服務方法可能會從測試中調用。為了避免這些困難,我們可以創建虛擬模塊來加載被測模塊。

例如,假設以下代碼表示一個添加了示例服務的模塊:

angular.module('sampleServices', [])
  .service('util', function() {
    this.isNumber = function(num) {
      return !isNaN(num);
    };

    this.isDate = function(date) {
      return (date instanceof Date);
    };
  });
登入後複製
登入後複製
登入後複製

以下代碼是示例服務的測試文件中的 beforeEach 塊:

module(function($provide) {
  $provide.service('util', function() {
    this.isNumber = jasmine.createSpy('isNumber').andCallFake(function(num) {
      // 模拟实现
    });
    this.isDate = jasmine.createSpy('isDate').andCallFake(function(num) {
      // 模拟实现
    });
  });
});

// 获取模拟服务的引用
var mockUtilSvc;

inject(function(util) {
  mockUtilSvc = util;
});
登入後複製
登入後複製
登入後複製

或者,我們也可以將服務的模擬實現添加到上面定義的虛擬模塊中。

模擬返回 Promise 的方法

如果不使用 Promise,編寫端到端的 Angular 應用程序可能很困難。測試依賴於返回 Promise 的方法的代碼片段成為一項挑戰。普通的 Jasmine 間諜會導致某些測試用例失敗,因為被測函數會期望一個具有實際 Promise 結構的對象。

可以使用另一個返回具有靜態值的 Promise 的異步方法來模擬異步方法。考慮以下工廠:

angular.module('mockingProviders',[])
  .provider('sample', function() {
    var registeredVals = [];

    this.register = function(val) {
      registeredVals.push(val);      
    };

    this.$get = function() {
      function getRegisteredVals() {
        return registeredVals;
      }

      return {
        getRegisteredVals: getRegisteredVals
      };
    };
  });
登入後複製
登入後複製
登入後複製

我們將測試上述工廠中的 getData() 函數。正如我們所看到的,它依賴於服務 dataSourceSvc 的方法 getAllItems()。我們需要在測試 getData() 方法的功能之前模擬服務和方法。

$q 服務具有 when()reject() 方法,允許使用靜態值來解析或拒絕 Promise。這些方法在模擬返回 Promise 的方法的測試中非常有用。以下代碼片段模擬了 dataSourceSvc 工廠:

module(function($provide) {
  $provide.provider('sample', function() {
    this.register = jasmine.createSpy('register');

    this.$get = function() {
      var getRegisteredVals = jasmine.createSpy('getRegisteredVals');

      return {
        getRegisteredVals: getRegisteredVals
      };
    };
  });
});

// 获取提供程序的引用
var sampleProviderObj;

module(function(sampleProvider) {
  sampleProviderObj = sampleProvider;
});
登入後複製
登入後複製
登入後複製

$q Promise 在下一個 digest 週期後完成其操作。 digest 週期在實際應用程序中不斷運行,但在測試中則不會。因此,我們需要手動調用 $rootScope.$digest() 以強制執行 Promise。以下代碼片段顯示了一個示例測試:

angular.module('first', ['second', 'third'])
  // util 和 storage 分别在 second 和 third 中定义
  .service('sampleSvc', function(utilSvc, storageSvc) {
    // 服务实现
  });
登入後複製

模擬全局對象

全局對象來自以下來源:

  1. 全局“window”對象的一部分的對象(例如,localStorage、indexedDb、Math 等)。
  2. 由第三方庫(如 jQuery、underscore、moment、breeze 或任何其他庫)創建的對象。

默認情況下,全局對象無法模擬。我們需要遵循某些步驟才能使它們可模擬。

我們可能不想模擬 Math 對像或 _(由 Underscore 庫創建)的實用程序對象,因為它們的操作不執行任何業務邏輯、不操作 UI,也不與數據源通信。但是,必須模擬諸如 $.ajax、localStorage、WebSockets、breeze 和 toastr 之類對象。因為如果沒有模擬這些對象,這些對象會在執行單元測試時執行其實際操作,這可能會導致一些不必要的 UI 更新、網絡調用,有時還會導致測試代碼中的錯誤。

由於依賴注入,Angular 中編寫的每一部分代碼都是可測試的。 DI 允許我們傳遞任何遵循實際對象 shim 的對象,只是為了使被測代碼在執行時不會中斷。如果可以注入全局對象,則可以模擬它們。有兩種方法可以使全局對象可注入:

  1. $window 注入到需要全局對象的 service/controller 中,並通過 $window 訪問全局對象。例如,以下服務通過 $window 使用 localStorage:
angular.module('sampleServices', [])
  .service('util', function() {
    this.isNumber = function(num) {
      return !isNaN(num);
    };

    this.isDate = function(date) {
      return (date instanceof Date);
    };
  });
登入後複製
登入後複製
登入後複製
  1. 使用全局對象創建一個值或常量,並在需要的地方注入它。例如,以下代碼是 toastr 的常量:
module(function($provide) {
  $provide.service('util', function() {
    this.isNumber = jasmine.createSpy('isNumber').andCallFake(function(num) {
      // 模拟实现
    });
    this.isDate = jasmine.createSpy('isDate').andCallFake(function(num) {
      // 模拟实现
    });
  });
});

// 获取模拟服务的引用
var mockUtilSvc;

inject(function(util) {
  mockUtilSvc = util;
});
登入後複製
登入後複製
登入後複製

我更喜歡使用常量而不是值來包裝全局對象,因為常量可以注入到配置塊或提供程序中,並且常量不能被裝飾。

以下代碼片段顯示了 localStorage 和 toastr 的模擬:

angular.module('mockingProviders',[])
  .provider('sample', function() {
    var registeredVals = [];

    this.register = function(val) {
      registeredVals.push(val);      
    };

    this.$get = function() {
      function getRegisteredVals() {
        return registeredVals;
      }

      return {
        getRegisteredVals: getRegisteredVals
      };
    };
  });
登入後複製
登入後複製
登入後複製

結論

模擬是在任何語言中編寫單元測試的重要組成部分之一。正如我們所看到的,依賴注入在測試和模擬中起著重要作用。代碼必須以一種方式組織,以便輕鬆測試其功能。本文列出了在測試 AngularJS 應用程序時模擬最常見的一組對象。與本文相關的代碼可從 GitHub 下載。

關於在 AngularJS 測試中模擬依賴項的常見問題解答 (FAQ)

在 AngularJS 測試中模擬依賴項的目的是什麼?

在 AngularJS 測試中模擬依賴項是單元測試的關鍵部分。它允許開發人員隔離被測代碼並模擬其依賴項的行為。這樣,您可以測試代碼如何與其依賴項交互,而無需實際調用它們。當依賴項複雜、緩慢或具有您希望在測試期間避免的副作用時,這尤其有用。通過模擬這些依賴項,您可以專注於在受控環境中測試代碼的功能。

如何在 AngularJS 中創建一個模擬服務?

在 AngularJS 中創建模擬服務涉及在模塊配置中使用 $provide 服務。您可以使用 $provide 服務的 valuefactoryservice 方法來定義服務的模擬實現。這是一個基本示例:

module(function($provide) {
  $provide.provider('sample', function() {
    this.register = jasmine.createSpy('register');

    this.$get = function() {
      var getRegisteredVals = jasmine.createSpy('getRegisteredVals');

      return {
        getRegisteredVals: getRegisteredVals
      };
    };
  });
});

// 获取提供程序的引用
var sampleProviderObj;

module(function(sampleProvider) {
  sampleProviderObj = sampleProvider;
});
登入後複製
登入後複製
登入後複製

在這個例子中,我們使用 $provide.value 方法來定義 myService 的模擬實現。在測試期間,將使用此模擬服務代替實際服務。

(其餘的FAQ問題,由於篇幅限制,請逐個提出,我會盡力提供簡潔明了的答案。)

以上是AngularJS測試中的模擬依賴項的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1317
25
PHP教程
1268
29
C# 教程
1248
24
JavaScript的演變:當前的趨勢和未來前景 JavaScript的演變:當前的趨勢和未來前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript引擎:比較實施 JavaScript引擎:比較實施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript:探索網絡語言的多功能性 JavaScript:探索網絡語言的多功能性 Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

如何使用Next.js(前端集成)構建多租戶SaaS應用程序 如何使用Next.js(前端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

使用Next.js(後端集成)構建多租戶SaaS應用程序 使用Next.js(後端集成)構建多租戶SaaS應用程序 Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

從C/C到JavaScript:所有工作方式 從C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

See all articles