プロバイダを単体テストする方法の例はありますか?
例えば:
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
が表示され、サービスとしてテストできます。しかし、どのように構成する機能をテストできますか?それともまったく必要ありませんか?
私はこれと同じ質問を持っていて、これで有効なソリューションを見つけました Google Group answer そしてそれは 中央の例 を参照しています。
プロバイダーコードのテストは、次のようになります( 中央の例 のコードに従って、何が機能したかを確認します)。
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');
});
});
});
私は@Mark 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');
}));
または$ injectorを使用して新しいインスタンスを作成し、
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');
}));
上記のどちらでも、it
ブロック内の個々のdescribe
ステートメントごとにプロバイダーを再構成できます。ただし、複数のテストに対して一度だけプロバイダーを構成する必要がある場合は、これを行うことができます。
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() {
...
});
@Stephane Catalaの答えは特に役に立ちました。私は彼のproviderGetterを使用して正確に欲しいものを取得しました。プロバイダーに初期化を実行させ、実際のサービスでさまざまな設定で物事が正しく機能していることを検証できるようにすることが重要でした。サンプルコード:
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');
});
});
ここに、フェッチプロバイダーを適切にカプセル化する小さなヘルパーがあります。したがって、個々のテスト間の分離を確保します。
/**
* @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
}
個人的には、この手法を使用して、外部ライブラリから提供されるプロバイダーをモックします。これは、すべてのテストのヘルパーファイルに含めることができます。もちろん、この質問のように、カスタムプロバイダーでも機能します。アイデアは、アプリによって呼び出される前に、モジュール内のプロバイダーを再定義することです
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();
}));
});
});
プロバイダーでいくつかの設定が正しく設定されていることをテストするだけでよいため、module()
を介してモジュールを初期化するときにプロバイダーを構成するためにAngular DIを使用しました。
上記のソリューションのいくつかを試した後、プロバイダーが見つからないという問題もありました。そのため、代替アプローチの必要性を強調しました。
その後、設定を使用して、新しい設定値の使用を反映していることを確認するテストを追加しました。
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);
});
});
});