javascript starter - 將模擬注入到AngularJS服務中




e2e test (7)

如果你的控制器被寫入來獲得這樣的依賴:

app.controller("SomeController", ["$scope", "someDependency", function ($scope, someDependency) {
    someDependency.someFunction();
}]);

那麼你可以在這樣的Jasmine測試中做一個假的someDependency

describe("Some Controller", function () {

    beforeEach(module("app"));


    it("should call someMethod on someDependency", inject(function ($rootScope, $controller) {
        // make a fake SomeDependency object
        var someDependency = {
            someFunction: function () { }
        };

        spyOn(someDependency, "someFunction");

        // this instantiates SomeController, using the passed in object to resolve dependencies
        controller("SomeController", { $scope: scope, someDependency: someDependency });

        expect(someDependency.someFunction).toHaveBeenCalled();
    }));
});

我寫了一個AngularJS服務,我想單元測試它。

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
    factory('myService', function ($http, fooService, barService) {

    this.something = function() {
        // Do something with the injected services
    };

    return this;
});

我的app.js文件有這些註冊的:

angular
.module('myApp', ['fooServiceProvider','barServiceProvider','myServiceProvider']
)

我可以測試DI是如此工作的:

describe("Using the DI framework", function() {
    beforeEach(module('fooServiceProvider'));
    beforeEach(module('barServiceProvider'));
    beforeEach(module('myServiceProvder'));

    var service;

    beforeEach(inject(function(fooService, barService, myService) {
        service=myService;
    }));

    it("can be instantiated", function() {
        expect(service).not.toBeNull();
    });
});

這證明了服務可以由DI框架創建,然而接下來我想單元測試服務,這意味著嘲笑注入的對象。

我如何去做這件事?

我試過把我的模擬對象放在模塊中,例如

beforeEach(module(mockNavigationService));

並將服務定義重寫為:

function MyService(http, fooService, barService) {
    this.somthing = function() {
        // Do something with the injected services
    };
});

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
    factory('myService', function ($http, fooService, barService) { return new MyService($http, fooService, barService); })

但後者似乎停止了由DI創建的服務。

有人知道我可以如何嘲笑我的單元測試注入的服務嗎?

謝謝

大衛


我看待它的方式,不需要自己嘲笑服務。 簡單地模擬服務的功能。 這樣,您就可以在整個應用程序中為您的實際服務注入角色。 然後,根據需要使用Jasmine的spyOn函數來模擬服務上的功能。

現在,如果服務本身是一個函數,而不是一個可以使用spyOn的對象,還有另一種方法可以解決它。 我需要做到這一點,並找到一些對我來說非常有用的東西。 請參閱如何模擬角色服務是一種功能?


在Angular和Jasmine中,另一種幫助簡化模擬依賴關係的方法是使用QuickMock。 它可以在GitHub上找到,並允許您以可重用的方式創建簡單的模擬。 你可以通過下面的鏈接從GitHub克隆它。 自述文件很自我解釋,但希望它可以在未來幫助其他人。

https://github.com/tennisgent/QuickMock

describe('NotificationService', function () {
    var notificationService;

    beforeEach(function(){
        notificationService = QuickMock({
            providerName: 'NotificationService', // the provider we wish to test
            moduleName: 'QuickMockDemo',         // the module that contains our provider
            mockModules: ['QuickMockDemoMocks']  // module(s) that contains mocks for our provider's dependencies
        });
    });
    ....

它會自動管理上面提到的所有樣板文件,因此您不必在每次測試中都寫出所有模擬注入代碼。 希望有所幫助。


您可以使用$provide將嘲笑注入到您的服務中。

如果您對具有名為getSomething的方法的依賴項具有以下服務:

angular.module('myModule', [])
  .factory('myService', function (myDependency) {
        return {
            useDependency: function () {
                return myDependency.getSomething();
            }
        };
  });

你可以注入一個模擬版本的myDependency,如下所示:

describe('Service: myService', function () {

  var mockDependency;

  beforeEach(module('myModule'));

  beforeEach(function () {

      mockDependency = {
          getSomething: function () {
              return 'mockReturnValue';
          }
      };

      module(function ($provide) {
          $provide.value('myDependency', mockDependency);
      });

  });

  it('should return value from mock dependency', inject(function (myService) {
      expect(myService.useDependency()).toBe('mockReturnValue');
  }));

});

請注意,由於對$provide.value的調用,您實際上並不需要在任何地方顯式註入myDependency。 它在註入myService期間發生在引擎蓋下。 在這裡設置mockDependency時,它可能很容易成為間諜。

感謝loyalBrown鏈接到偉大的視頻


除了John Galambos的回答 :如果你只是想嘲笑服務的具體方法,你可以這樣做:

describe('Service: myService', function () {

  var mockDependency;

  beforeEach(module('myModule'));

  beforeEach(module(function ($provide, myDependencyProvider) {
      // Get an instance of the real service, then modify specific functions
      mockDependency = myDependencyProvider.$get();
      mockDependency.getSomething = function() { return 'mockReturnValue'; };
      $provide.value('myDependency', mockDependency);
  });

  it('should return value from mock dependency', inject(function (myService) {
      expect(myService.useDependency()).toBe('mockReturnValue');
  }));

});

我最近發布了ngImprovedTesting,它應該讓AngularJS的模擬測試變得更容易。

要測試'myService'(來自“myApp”模塊)及其fooService和barService依賴關係,您可以簡單地在Jasmine測試中執行以下操作:

beforeEach(ModuleBuilder
    .forModule('myApp')
    .serviceWithMocksFor('myService', 'fooService', 'barService')
    .build());

有關ngImprovedTesting的更多信息,請查看其介紹性博客文章: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/ ://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/


AngularJS會記住該值,並將其與以前的值進行比較。 這是基本的髒檢查。 如果價值發生變化,則會觸髮變化事件。

$apply()方法,這是您在從非AngularJS世界轉換到AngularJS世界時所稱的$digest() 。 摘要只是簡單的舊臟檢。 它適用於所有瀏覽器,並且完全可以預測。

為了比較髒檢查(AngularJS)和更改監聽器( KnockoutJSBackbone.js ):雖然臟檢查可能看起來很簡單,甚至效率低下(我將在後面解決),但事實證明,它始終在語義上是正確的,而更改監聽器有很多怪異的角落案例,並且需要諸如依賴關係跟踪之類的東西來使其更加符合語義。 KnockoutJS依賴關係跟踪是AngularJS沒有的一個聰明的特性。

更改監聽器的問題:

  • 語法很殘酷,因為瀏覽器本身不支持它。 是的,有代理,但在所有情況下,它們在語義上都不正確,當然,舊瀏覽器上也沒有代理。 底線是臟檢查允許你做POJO ,而KnockoutJS和Backbone.js強迫你從它們的類繼承,並通過訪問器訪問你的數據。
  • 改變合併。 假設你有一個項目數組。 假設你想添加項目到一個數組中,當你循環添加時,每次你添加的時候你正在觸發改變的事件,這正在渲染UI。 這對性能非常不利。 你想要的只是在最後更新UI。 變化事件太細緻。
  • 更改偵聽器立即在setter上觸發,這是一個問題,因為更改偵聽器可以進一步更改數據,這會觸發更多的更改事件。 這很糟糕,因為在您的堆棧中,您可能會同時發生多個更改事件。 假設你有兩個數組需要保持同步,無論出於何種原因。 您只能添加到其中一個,但每次添加時都會觸發一個更改事件,該事件現在對世界有不一致的看法。 這與線程鎖定非常類似,因為每個回調都是專門執行並完成的,所以JavaScript避免了這種情況。 變更事件破壞了這一點,因為變更者可能產生意義深遠的後果,而這些後果並非有意且非明顯,從而再次產生線程問題。 事實證明,你想要做的就是延遲監聽器的執行,並且保證一次只能運行一個監聽器,因此任何代碼都可以自由地改變數據,並且它知道其他代碼在運行時不會運行。

性能如何?

所以看起來我們很慢,因為臟檢是無效的。 這是我們需要查看實數而不是僅僅具有理論參數的地方,但首先讓我們定義一些約束條件。

人類是:

  • - 任何超過50毫秒的速度都不會被人看到,因此可以被視為“即時”。

  • 有限 - 您無法在單個頁面上向人顯示超過2000條信息。 除此之外的任何東西都是非常糟糕的用戶界面,人類無法處理這一點。

所以真正的問題是:在50毫秒內你可以在瀏覽器上做多少次比較? 這是一個很難回答的問題,因為許多因素起作用,但這裡是一個測試用例: http://jsperf.com/angularjs-digest/6 : http://jsperf.com/angularjs-digest/6創建10,000個觀察者。 在現代瀏覽器上,這需要不到6毫秒。 在Internet Explorer 8大約需要40毫秒。 正如你所看到的,這些日子即使在慢速瀏覽器上也不是問題。 有一個警告:比較需要很簡單,以適應時間限制...不幸的是,向AngularJS添加一個緩慢的比較太容易了,所以當你不知道你是什麼時,很容易構建緩慢的應用程序是做。 但是我們希望通過提供一個儀器模塊來給出答案,這個模塊會告訴你哪些是比較慢的。

事實證明,視頻遊戲和GPU使用臟檢查方法,特別是因為它是一致的。 只要它們超過顯示器刷新頻率(通常為50-60 Hz,或每16.6-20毫秒),任何超過這個性能的結果都是浪費,因此與提高FPS相比,您最好繪製更多的內容。





javascript angularjs mocking jasmine angularjs-service