Angular 角度测试-滴答不响';如果定时器处于组件初始化状态,则无法工作 问题:

Angular 角度测试-滴答不响';如果定时器处于组件初始化状态,则无法工作 问题:,angular,typescript,testing,timer,Angular,Typescript,Testing,Timer,我如何让勾选开始工作,或者至少如何让测试提前10秒,以便在我的组件中正确调用提交 注意:我不想做等待新的承诺(r=>setTimeout(r,10000))因为它会让我的测试运行得很长,而测试应该很短 目标 我希望submit仅在组件创建10秒后调用cb 描述 我的组件中有一个计时器,它在10秒后完成。此计时器将主题从false更改为true,并用于确定提交组件中的数据是否有效 在测试中,tick似乎根本没有提前定时器,实际上它运行了整整10秒。我试图通过在每个之前的中放置一个fakeAsync

我如何让
勾选
开始工作,或者至少如何让测试提前10秒,以便在我的组件中正确调用
提交

注意:我不想做
等待新的承诺(r=>setTimeout(r,10000))
因为它会让我的测试运行得很长,而测试应该很短

目标 我希望
submit
仅在组件创建10秒后调用
cb

描述 我的组件中有一个计时器,它在10秒后完成。此计时器将主题从false更改为true,并用于确定提交组件中的数据是否有效

在测试中,
tick
似乎根本没有提前定时器,实际上它运行了整整10秒。我试图通过在每个之前的
中放置一个
fakeAsync
来修复此问题,但没有成功创建组件

我试过的
  • 在测试组件init中使用
    fakeAsync
    ,以及测试
  • 仅在测试中使用
    fakeAsync
  • 使用
    setTimeout(()=>this.obs.next(true),10_000)
    代替计时器
  • 使用
    empty().pipe(延迟(10000)).subscribe(()=>this.obs.next(true))而不是计时器
  • 计时器
    放入
    ngOnInit
    而不是构造函数
  • 计时器
    置于构造函数中,而不是
    ngOnInit
观察 如果您调整此代码

timer(10_000).subscribe(()=>this.testThis$.next(true));
而是这样

计时器(10_000).订阅(()=>{
调试器;
this.testThis$.next(true)
});
您会发现,每次测试运行时,Dev工具中的Javascript调试器都会在组件创建10秒后触发(如果勾选成功,则不会立即触发)

代码 这是密码。底部是指向GitHub上最小复制的链接

//组件代码
从“@angular/core”导入{Component,OnInit,Inject};
从“rxjs”导入{BehaviorSubject,Subject,timer};
从“rxjs/operators”导入{first,filter};
@组成部分({
选择器:“应用程序勾选测试”,
templateUrl:“./tick test.component.html”,
样式URL:['./勾选test.component.scss']
})
导出类TickTestComponent实现OnInit{
公共测试此$:主题;
建造师(
@注入('TICK_CALLBACK')私有只读cb:()=>void,
) {
this.testThis$=新行为主体(false);
计时器(10_000).subscribe(()=>this.testThis$.next(true));
}
public ngOnInit():void{
}
公共提交():无效{
//10秒后调用回调
这个,测试这个$
.pipe(第一个(),过滤器(a=>!!a))
.subscribe(()=>this.cb());
}
}
//测试代码
/**
*这一次的问题是,我希望“滴答”能提高效率
*在构造函数中创建的计时器的时间,但它不工作
*/
从“@angular/core/testing”导入{async,ComponentFixture,TestBed,tick,fakeAsync};
从“./tick test.component”导入{tick testcomponent};
描述('TickTestComponent',()=>{
let组件:TickTestComponent;
let夹具:组件夹具;
让我们回拨:茉莉花。间谍;
beforeach(异步(()=>{
callback=jasmine.createSpy('TICK_callback');
TestBed.configureTestingModule({
供应商:[
{提供:'TICK_CALLBACK',useValue:CALLBACK},
],
声明:[TickTestComponent]
})
.compileComponents();
}));
在每个之前(()=>{
fixture=TestBed.createComponent(TickTestComponent);
组件=fixture.componentInstance;
fixture.detectChanges();
});
它('应该创建',()=>{
expect(component.toBeTruthy();
});
它('在10秒后应为true',fakeAsync(()=>{
勾选(10_001);
component.submit();
expect(callback).toHaveBeenCalled();
}));
});
最小再生产回购

解决方案
  • fixture.detectChanges()
    移动到每个测试中,并在那里调用
    勾选(10_000)
  • 定时器(10_000).
    移动到组件中的
    ngOnInit
  • 发生了什么事 无论何时使用
    fakeAsync
    都会创建一个代码可以在其中运行的“区域”。根据我的观察,这个区域“存在”,直到它超出范围。通过在每个
    之前的
    中使用
    fakeAsync
    ,您会破坏该区域,并且会出现计时器未完成的问题(尽管计时器未完成是理想的结果)

    您希望将
    计时器
    移动到
    ngOnInit
    中,因为在调用
    .createComponent
    时不会立即调用它。而是在您第一次运行
    fixture.detectChanges()
    时调用它。因此,当您第一次在测试的
    fakeAsync
    区域内调用
    fixture.detectChanges()
    时,会为您调用
    ngOnInit
    ,计时器会在该区域中捕获,您可以按预期控制时间

    代码
    description('TickTestComponent',()=>{
    let组件:TickTestComponent;
    let夹具:组件夹具;
    让我们回拨:茉莉花。间谍;
    beforeach(异步(()=>{
    callback=jasmine.createSpy('TICK_callback');
    TestBed.configureTestingModule({
    供应商:[
    {提供:'TICK_CALLBACK',useValue:CALLBACK},
    ],
    声明:[TickTestComponent]
    })
    .compileComponents();
    }));
    在每个之前(()=>{
    fixture=TestBed.createComponent(TickTestComponent);
    组件=fixture.componentInstance;
    //不要在这里运行这个
    //fixture.detectChanges();
    });
    它('应该创建',()=>{
    expect(component.toBeTruthy();
    });
    它('在10秒后应为true',fakeAsync(()=>{
    //如果这是第一个detectChanges调用,则调用ngOnInit
    fixture.detectChanges();
    勾选(10_001);
    component.submit();
    预期(约
    
    describe('TickTestComponent', () => {
      let component: TickTestComponent;
      let fixture: ComponentFixture<TickTestComponent>;
    
      let callback: jasmine.Spy;
    
      beforeEach(async(() => {
    
        callback = jasmine.createSpy('TICK_CALLBACK');
    
        TestBed.configureTestingModule({
          providers: [
            { provide: 'TICK_CALLBACK', useValue: callback },
          ],
          declarations: [ TickTestComponent ]
        })
        .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(TickTestComponent);
        component = fixture.componentInstance;
        // don't run this here
        // fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    
      it('should be true after 10s', fakeAsync(() => {
        // this calls ngOnInit if it is the first detectChanges call
        fixture.detectChanges();
        tick(10_001);
    
        component.submit();
        expect(callback).toHaveBeenCalled();
      }));
    });