我正在遵循https://angular.io/guide/testing的官方“测试”指南,为应用程序编写测试。
在测试具有依赖项的服务时,我需要为这些依赖项提供模拟,但是有趣的是,这对于ApplicationRef似乎并不正确,我真的很想知道为什么。
该服务是这样的:
export class MyService {
constructor(
private dependentService: DependentService,
private applicationRef:ApplicationRef
){}
...
以及相应的测试规范:
describe('MyService', () => {
let dependentServiceSpy: jasmine.SpyObj<HttpClient>;
beforeEach(() => {
const dependentServiceSpy = jasmine.createSpyObj('DependentService', ['test']);
TestBed.configureTestingModule({
// Provide both the service-to-test and its (spy) dependency
// why is 'ApplicationRef' not needed here??
providers: [
MyService,
{ provide: DependentService, useValue: dependentService_spy }
]
});
});
...
});
由于在MyService构造函数中同时注入了'DependentService'和'ApplicationRef',因此我希望在TestBed的providers数组中都需要这两者。但是,虽然忽略了“ DependentService”,但会在测试中产生错误,而缺少的“ ApplicationRef”不会。
对此有合理的解释吗?
答案 0 :(得分:2)
每个使用TestBed的Angular测试配置都从以下初始化开始:
<script type="text/javascript">
importeReal = parseFloat({{$importe}});
$(function() {
setTimeout(chequear, 1000);
});
function chequear() {
$('input[data-denominacion]').each(function(index) {
$(this).bind('change', function() {
var valorInput = parseFloat($(this).val());
var denominacion = parseFloat($(this).attr('data-denominacion'));
if (/\D/.test(valorInput) || /\s/.test(valorInput)) {
toastr.warning('Solo valores numericos, por favor');
$(this).addClass('active');
}
if(/\d/.test(valorInput)) {
var denominacionXInputValue = valorInput * denominacion;
importeReal = importeReal - denominacionXInputValue;
console.log(importeReal);
if(importeReal <= 0) {
toastr.warning('No puede poner mas denominaciones');
} else {
toastr.info('Actualmente tienes sin ingresar' + importeReal);
}
}
if(valorInput =='') {
importeReal = importeReal + denominacionXInputValue;
toastr.info('Actualmente tienes sin ingresar' + importeReal);
}
});
});
}
</script>
Angular测试环境将使用BrowserDynamicTestingModule构造注射器,因为它适用于普通Angular模块(另请参见https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)。
Angular合并getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
包含的提供程序。该模块声明如下:
BrowserDynamicTestingModule
@NgModule({
exports: [BrowserTestingModule],
providers: [
{provide: TestComponentRenderer, useClass: DOMTestComponentRenderer},
]
})
export class BrowserDynamicTestingModule {
}
如下:
BrowserTestingModule
和 /\
||
@NgModule({
exports: [BrowserModule],
providers: [
{provide: APP_ID, useValue: 'a'},
ELEMENT_PROBE_PROVIDERS,
{provide: NgZone, useFactory: createNgZone},
]
})
export class BrowserTestingModule {
}
是:
BrowserModule
最后 /\
||
@NgModule({providers: BROWSER_MODULE_PROVIDERS, exports: [CommonModule, ApplicationModule]})
export class BrowserModule {
...
}
声明ApplicationModule
:
ApplicationRef
并且正如您所见, /\
||
export const APPLICATION_MODULE_PROVIDERS: StaticProvider[] = [
{
provide: ApplicationRef,
useClass: ApplicationRef,
deps:
[NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus]
},
...
];
@NgModule({providers: APPLICATION_MODULE_PROVIDERS})
export class ApplicationModule {
// Inject ApplicationRef to make it eager...
constructor(appRef: ApplicationRef) {}
}
提供者也被热切实例化。
这样,这里就没有魔术了,Angular使用的算法与从用户定义的NgModules中解析提供程序的算法相同
答案 1 :(得分:1)
我不得不猜测,但是我认为:
每个组件都隐式包含更改检测。如果不是,则不会进行自动更改检测。 但是要直接在代码中使用此功能,必须在构造函数中显式“捕获”该服务。
现在,我们的TestBed将创建您在此处明确定义的服务。但是它还将创建所有这些内部需要的服务(例如ChangeDetection,请参见经典的“ fixture.detectChanges()”)。 因此,您不必显式创建它。
您提到的“ DependentService”不是内部Angular魔术的一部分,因此您必须在TestBed中明确命名它。
热烈的问候
答案 2 :(得分:1)
这可能是因为我们将ApplicationRef
注入了constructor
内而没有在任何地方提供它。这是正常的interface
,而不是provider
。我们仅提供测试providers
内部的providers
数组,而不提供我们注入的所有数组。
ChangeDetectorRef
也是如此。我们永远不会在providers
数组中提供该数组。