2013-02-08 47 views
50

有沒有人有如何單元測試提供者的例子?如何測試AngularJS自定義提供者

例如:

config.js

angular.module('app.config', []) 
    .provider('config', function() { 
    var config = { 
      mode: 'distributed', 
      api: 'path/to/api' 
     }; 

    this.mode = function (type) { 
     if (type) { 
     config.isDistributedInstance = type === config.mode; 
     config.isLocalInstance = !config.isDistributedInstance; 
     config.mode = type; 
     return this; 
     } else { 
     return config.mode; 
     } 
    }; 

    this.$get = function() { 
     return config; 
    }; 
    }]); 

app.js

angular.module('app', ['app.config']) 
    .config(['configProvider', function (configProvider) { 
    configProvider.mode('local'); 
    }]); 

app.js是使用測試和我看到已經配置configProvider和我可以測試它作爲一項服務。但是我怎樣才能測試配置的能力?或者它根本不需要?

回答

63

我有這個相同的問題,只發現Google Group answer工作解決方案,它被引用fiddle example

測試你的供應商代碼看起來像這樣(在fiddle example的代碼,什麼工作對我來說以下):

describe('Test app.config provider', function() { 

    var theConfigProvider; 

    beforeEach(function() { 
     // Initialize the service provider 
     // by injecting it to a fake module's config block 
     var fakeModule = angular.module('test.app.config', function() {}); 
     fakeModule.config(function (configProvider) { 
      theConfigProvider = configProvider; 
     }); 
     // Initialize test.app injector 
     module('app.config', 'test.app.config'); 

     // Kickstart the injectors previously registered 
     // with calls to angular.mock.module 
     inject(function() {}); 
    }); 

    describe('with custom configuration', function() { 
     it('tests the providers internal function', function() { 
      // check sanity 
      expect(theConfigProvider).not.toBeUndefined(); 
      // configure the provider 
      theConfigProvider.mode('local'); 
      // test an instance of the provider for 
      // the custom configuration changes 
      expect(theConfigProvider.$get().mode).toBe('local'); 
     }); 
    }); 

}); 
+0

感謝您對這個職位!我按照這裏找到的指南,並失敗:https://github.com/angular/angular.js/issues/2274。 上述示例按預期工作。謝謝! – zayquan 2014-10-26 23:42:35

+0

當這樣做時,我不得不改變假模塊的聲明來傳遞一個空數組而不是一個空函數。可能是由於更新版本的Angular。 – 2016-10-13 14:44:50

45

我一直在使用@馬克Gemmill的解決方案,它工作得很好,但隨後偶然發現了這個略微冗長的解決方案,它不需要僞造模塊。

https://stackoverflow.com/a/15828369/1798234

所以,

var provider; 

beforeEach(module('app.config', function(theConfigProvider) { 
    provider = theConfigProvider; 
})) 

it('tests the providers internal function', inject(function() { 
    provider.mode('local') 
    expect(provider.$get().mode).toBe('local'); 
})); 


如果您的提供商$ get方法有依賴性,你可以把它們放入手動,

var provider; 

beforeEach(module('app.config', function(theConfigProvider) { 
    provider = theConfigProvider; 
})) 

it('tests the providers internal function', inject(function(dependency1, dependency2) { 
    provider.mode('local') 
    expect(provider.$get(dependency1, dependency2).mode).toBe('local'); 
})); 


或者使用$噴油器創建新實例,

var provider; 

beforeEach(module('app.config', function(theConfigProvider) { 
    provider = theConfigProvider; 
})) 

it('tests the providers internal function', inject(function($injector) { 
    provider.mode('local') 
    var service = $injector.invoke(provider); 
    expect(service.mode).toBe('local'); 
})); 


的上述兩個也會讓你重新配置提供者在describe塊每一個人it聲明。但是,如果你只需要一次多個測試配置提供,你可以做到這一點,

var service; 

beforeEach(module('app.config', function(theConfigProvider) { 
    var provider = theConfigProvider; 
    provider.mode('local'); 
})) 

beforeEach(inject(function(theConfig){ 
    service = theConfig; 
})); 

it('tests the providers internal function', function() { 
    expect(service.mode).toBe('local'); 
}); 

it('tests something else on service', function() { 
    ... 
}); 
2

這裏是一個小幫手,妥善封裝取提供商,從而確保各個測試之間的隔離:

/** 
    * @description request a provider by name. 
    * IMPORTANT NOTE: 
    * 1) this function must be called before any calls to 'inject', 
    * because it itself calls 'module'. 
    * 2) the returned function must be called after any calls to 'module', 
    * because it itself calls 'inject'. 
    * @param {string} moduleName 
    * @param {string} providerName 
    * @returns {function} that returns the requested provider by calling 'inject' 
    * usage examples: 
    it('fetches a Provider in a "module" step and an "inject" step', 
     function() { 
     // 'module' step, no calls to 'inject' before this 
     var getProvider = 
     providerGetter('module.containing.provider', 'RequestedProvider'); 
     // 'inject' step, no calls to 'module' after this 
     var requestedProvider = getProvider(); 
     // done! 
     expect(requestedProvider.$get).toBeDefined(); 
    }); 
    * 
    it('also fetches a Provider in a single step', function() { 
     var requestedProvider = 
     providerGetter('module.containing.provider', 'RequestedProvider')(); 

     expect(requestedProvider.$get).toBeDefined(); 
    }); 
    */ 
    function providerGetter(moduleName, providerName) { 
    var provider; 
    module(moduleName, 
      [providerName, function(Provider) { provider = Provider; }]); 
    return function() { inject(); return provider; }; // inject calls the above 
    } 
  • 獲取提供程序的過程已完全封裝:不需要關閉變量,以減少測試之間的隔離。
  • 該過程可以分爲兩個步驟,一個「模塊」步驟和一個「注入」步驟,可以在單元測試中分別與其他對'模塊'和'注入'的調用分組。
  • 如果不需要分割,檢索提供者可以簡單地在一個命令中完成!
3

@Stephane Catala的回答特別有用,我用他的提供者獲取我想要的東西。能夠讓供應商進行初始化,然後通過實際的服務來驗證各種設置能夠正常工作,這一點很重要。示例代碼:

angular 
     .module('test', []) 
     .provider('info', info); 

    function info() { 
     var nfo = 'nothing'; 
     this.setInfo = function setInfo(s) { nfo = s; }; 
     this.$get = Info; 

     function Info() { 
      return { getInfo: function() {return nfo;} }; 
     } 
    } 

茉莉花測試規範:

describe("provider test", function() { 

     var infoProvider, info; 

     function providerGetter(moduleName, providerName) { 
      var provider; 
      module(moduleName, 
         [providerName, function(Provider) { provider = Provider; }]); 
      return function() { inject(); return provider; }; // inject calls the above 
     } 

     beforeEach(function() { 
      infoProvider = providerGetter('test', 'infoProvider')(); 
     }); 

     it('should return nothing if not set', function() { 
      inject(function(_info_) { info = _info_; }); 
      expect(info.getInfo()).toEqual('nothing'); 
     }); 

     it('should return the info that was set', function() { 
      infoProvider.setInfo('something'); 
      inject(function(_info_) { info = _info_; }); 
      expect(info.getInfo()).toEqual('something'); 
     }); 

    }); 
+0

其他人可以隨時刪除他們的答案。所以,你的答案不應該提到任何其他答案。 – manetsus 2015-07-19 04:04:52

+2

只會在信貸到期時給予貸款。我的答案在沒有以前的答案的情況下是完整的,儘管它在providerGetter中進一步深入 - 但我花了一段時間才弄清楚如何測試提供者階段和服務階段(這對於我的真實情況非常重要),所以以爲我會在這裏扔它 - 沒有其他答案似乎測試設置和使用。 – ScottG 2015-07-20 21:53:42

+0

這很好,但信貸會去信貸的地方。在這種情況下,如果您有權放棄投票,信用證的地方將放棄投票。否則,等待放棄投票的機會。你的答案可以是更好的答案,但這不是我的話題(因爲我參與了這裏的審查你的問題),它不是正確的地方給予其他答案。欲瞭解更多信息,請加入meta SO並瞭解更多關於社區標準的信息。 @ScottG – manetsus 2015-07-20 23:40:56

1

我個人使用這種技術來嘲笑提供商外部庫,你可以把在所有測試的輔助文件來了。當然,它也可以在自定義提供者中工作。我們的想法是重新定義自己的模塊中的提供者之前它是由應用程序稱爲

describe('app', function() { 
    beforeEach(module('app.config', function($provide) { 
    $provide.provider('config', function() { 
     var mode = jasmine.createSpy('config.mode'); 

     this.mode = mode; 

     this.$get = function() { 
     return { 
      mode: mode 
     }; 
     }; 
    }); 
    })); 

    beforeEach(module('app')); 

    describe('.config', function() { 
    it('should call config.mode', inject(function(config) { 
     expect(config.mode).toHaveBeenCalled(); 
    })); 
    }); 
}); 
1

我只需要測試正在正確設置提供一些設置,所以我用角DI配置提供程序時我正在通過module()初始化模塊。

在嘗試了上述某些解決方案之後,我還發現提供程序未找到問題,因此強調需要採用其他方法。

在那之後,我加了進一步的測試,所使用的設置,以檢查它們反映了使用新的設定值。

describe("Service: My Service Provider", function() { 
    var myService, 
     DEFAULT_SETTING = 100, 
     NEW_DEFAULT_SETTING = 500; 

    beforeEach(function() { 

     function configurationFn(myServiceProvider) { 
      /* In this case, `myServiceProvider.defaultSetting` is an ES5 
      * property with only a getter. I have functions to explicitly 
      * set the property values. 
      */ 
      expect(myServiceProvider.defaultSetting).to.equal(DEFAULT_SETTING); 

      myServiceProvider.setDefaultSetting(NEW_DEFAULT_SETTING); 

      expect(myServiceProvider.defaultSetting).to.equal(NEW_DEFAULT_SETTING); 
     } 

     module("app", [ 
      "app.MyServiceProvider", 
      configurationFn 
     ]); 

     function injectionFn(_myService) { 
      myService = _myService; 
     } 

     inject(["app.MyService", injectionFn]); 
    }); 

    describe("#getMyDefaultSetting", function() { 

     it("should test the new setting", function() { 
      var result = myService.getMyDefaultSetting(); 

      expect(result).to.equal(NEW_DEFAULT_SETTING); 
     }); 

    }); 

}); 
相關問題