如何正确测试 $injector 的使用?
How to test properly the usage of $injector?
我有一项服务具有以下功能
injectService(serviceToInject: string, methodToInvoke: string){
let service = this.$injector.get(serviceToInject);
service[methodToInvoke]();
}
我想知道如何测试这个?我试过了:
(function () {
'use strict';
describe.only('ServiceA tests', function () {
let ServiceA;
beforeEach(angular.mock.module('main'));
beforeEach(inject(function (_ ServiceA_, _$injector_) {
ServiceA = _ServiceA_;
$injector = _$injector_;
}));
describe.only('injectServiceAndInvoke', function () {
it('given a string serviceToInject which is a valid service name and a string methodToInvoke which is valid method name without parameters, it should inject the service and call the method', () => {
let serviceName = 'validServiceName';
let methodWithoutParams = 'method';
let injectedService = $injector.get(serviceName);
// sandboxSinon.stub(ButtonService.$injector, 'get').withArgs(serviceName).returns(stubbedService);
let methodToBeCalled = sandboxSinon.stub(injectedService, methodWithoutParams).withArgs(undefined);
sandboxSinon.stub(ServiceA, 'tokenizeState').withArgs(methodWithoutParams).returns([methodWithoutParams, undefined]);
ServiceA.injectServiceAndInvoke(serviceName, methodWithoutParams);
expect(methodToBeCalled.calledOnce).to.equal(true);
});
});
});
})();
我得到的错误是(正确)服务 'validServiceName' 不存在。我也尝试存根 $injector.get 但我不明白 return 这个存根应该是什么以及如何从这个服务调用方法。
由于 $injector
服务是全局使用的,因此不能通过 DI 完全模拟。这是真正隔离的单元测试的障碍。但这并不是一件坏事,因为单个条件模拟不会使测试变得脆弱:
const injectedService = { methodName: sinon.stub() };
sinon.stub($injector, 'get');
$injector.get.withArgs('injectedServiceName').returns(injectedService)
$injector.get.callThrough();
ServiceA.injectServiceAndInvoke('injectedServiceName', 'methodName');
expect($injector.get.withArgs('injectedServiceName').calledOnce).to.equal(true);
expect(injectedService.methodName.calledOnce).to.equal(true);
expect(injectedService.methodName.calledWith()).to.equal(true);
但是由于服务有 $injector
作为 属性,这为测试提供了一个很好的选择,因为 属性 可以在服务实例化之后被模拟,而不是模拟真实的 [=14] =]:
const injectedService = { methodName: sinon.stub() };
const injectorMock = { get: sinon.stub() };
injectorMock.get.withArgs('injectedServiceName').returns(injectedService);
ServiceA.$injector = injectorMock;
ServiceA.injectServiceAndInvoke('injectedServiceName', 'methodName');
expect(injectorMock.get.withArgs('injectedServiceName').calledOnce).to.equal(true);
expect(injectedService.methodName.calledOnce).to.equal(true);
expect(injectedService.methodName.calledWith()).to.equal(true);
我有一项服务具有以下功能
injectService(serviceToInject: string, methodToInvoke: string){
let service = this.$injector.get(serviceToInject);
service[methodToInvoke]();
}
我想知道如何测试这个?我试过了:
(function () {
'use strict';
describe.only('ServiceA tests', function () {
let ServiceA;
beforeEach(angular.mock.module('main'));
beforeEach(inject(function (_ ServiceA_, _$injector_) {
ServiceA = _ServiceA_;
$injector = _$injector_;
}));
describe.only('injectServiceAndInvoke', function () {
it('given a string serviceToInject which is a valid service name and a string methodToInvoke which is valid method name without parameters, it should inject the service and call the method', () => {
let serviceName = 'validServiceName';
let methodWithoutParams = 'method';
let injectedService = $injector.get(serviceName);
// sandboxSinon.stub(ButtonService.$injector, 'get').withArgs(serviceName).returns(stubbedService);
let methodToBeCalled = sandboxSinon.stub(injectedService, methodWithoutParams).withArgs(undefined);
sandboxSinon.stub(ServiceA, 'tokenizeState').withArgs(methodWithoutParams).returns([methodWithoutParams, undefined]);
ServiceA.injectServiceAndInvoke(serviceName, methodWithoutParams);
expect(methodToBeCalled.calledOnce).to.equal(true);
});
});
});
})();
我得到的错误是(正确)服务 'validServiceName' 不存在。我也尝试存根 $injector.get 但我不明白 return 这个存根应该是什么以及如何从这个服务调用方法。
由于 $injector
服务是全局使用的,因此不能通过 DI 完全模拟。这是真正隔离的单元测试的障碍。但这并不是一件坏事,因为单个条件模拟不会使测试变得脆弱:
const injectedService = { methodName: sinon.stub() };
sinon.stub($injector, 'get');
$injector.get.withArgs('injectedServiceName').returns(injectedService)
$injector.get.callThrough();
ServiceA.injectServiceAndInvoke('injectedServiceName', 'methodName');
expect($injector.get.withArgs('injectedServiceName').calledOnce).to.equal(true);
expect(injectedService.methodName.calledOnce).to.equal(true);
expect(injectedService.methodName.calledWith()).to.equal(true);
但是由于服务有 $injector
作为 属性,这为测试提供了一个很好的选择,因为 属性 可以在服务实例化之后被模拟,而不是模拟真实的 [=14] =]:
const injectedService = { methodName: sinon.stub() };
const injectorMock = { get: sinon.stub() };
injectorMock.get.withArgs('injectedServiceName').returns(injectedService);
ServiceA.$injector = injectorMock;
ServiceA.injectServiceAndInvoke('injectedServiceName', 'methodName');
expect(injectorMock.get.withArgs('injectedServiceName').calledOnce).to.equal(true);
expect(injectedService.methodName.calledOnce).to.equal(true);
expect(injectedService.methodName.calledWith()).to.equal(true);