有没有人举例说明如何对提供商进行单元测试?
例如:
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
,我可以将其作为服务进行测试。但是我如何测试配置能力?或者根本不需要它?
答案 0 :(得分:64)
我有同样的问题,只在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');
});
});
});
答案 1 :(得分:46)
我一直在使用@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');
}));
如果您的providers $ 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() {
...
});
答案 2 :(得分:3)
@Stephane Catala的回答特别有帮助,我使用他的providerGetter来获得我想要的东西。能够让提供程序进行初始化,然后实际服务以验证事物是否正常运行各种设置非常重要。示例代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
茉莉花测试规范:
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;} };
}
}
答案 3 :(得分: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
}
答案 4 :(得分: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();
}));
});
});
答案 5 :(得分:1)
我只需要测试某些设置是否在提供程序上正确设置,因此我在通过fatal error: unexpectedly found nil while unwrapping an Optional value
初始化模块时使用Angular DI来配置提供程序。
在尝试了上述某些解决方案后,我也遇到了一些未找到提供商的问题,因此强调需要采用其他方法。
之后,我添加了进一步的测试,使用这些设置来检查它们是否反映了新设置值的使用。
module()