angular - Angular 测试 - 如果计时器在组件初始化中,Tick 不起作用
问题描述
问题
我如何开始tick
工作,或者至少,如何让测试提前 10 秒以适当地调用submit
我的组件?
注意:我不想这样做,await new Promise(r => setTimeout(r, 10000))
因为它会使我的测试运行很长时间,而测试应该很短
目标
我只想在创建组件后 10 秒后submit
调用cb
描述
我的组件中有一个计时器,它在 10 秒后完成。此计时器将主题从假更改为真,并用于确定提交组件中的数据是否被认为是有效的。
在测试中,tick
似乎根本没有提前定时器,它实际上运行了整整 10 秒。我试图通过fakeAsync
在beforeEach
创建组件的中添加一个无济于事来解决这个问题。
我试过的
- 在测试组件init中使用
fakeAsync
,以及测试 fakeAsync
仅在测试中使用- 使用
setTimeout(() => this.obs.next(true), 10_000)
而不是计时器 - 使用
empty().pipe(delay(10000)).subscribe(() => this.obs.next(true));
而不是计时器 - 放入
timer
而ngOnInit
不是构造函数 - 放入
timer
构造函数而不是ngOnInit
观察
如果您调整此代码
timer(10_000).subscribe(() => this.testThis$.next(true));
改为这样
timer(10_000).subscribe(() => {
debugger;
this.testThis$.next(true)
});
您会发现每次运行测试时,Dev Tools 中的 Javascript 调试器都会在组件创建后 10 秒触发(而不是在 tick 有效时立即触发)。
代码
这是代码。底部是 GitHub 上最小复制的链接。
// component code
import { Component, OnInit, Inject } from '@angular/core';
import { BehaviorSubject, Subject, timer } from 'rxjs';
import { first, filter } from 'rxjs/operators';
@Component({
selector: 'app-tick-test',
templateUrl: './tick-test.component.html',
styleUrls: ['./tick-test.component.scss']
})
export class TickTestComponent implements OnInit {
public testThis$: Subject<boolean>;
constructor(
@Inject('TICK_CALLBACK') private readonly cb: () => void,
) {
this.testThis$ = new BehaviorSubject<boolean>(false);
timer(10_000).subscribe(() => this.testThis$.next(true));
}
public ngOnInit(): void {
}
public submit(): void {
// call the callback after 10s
this.testThis$
.pipe(first(), filter(a => !!a))
.subscribe(() => this.cb());
}
}
// test code
/**
* The problem in this one is that I am expecting `tick` to advance the
* time for the timer that was created in the constructor, but it is not working
*/
import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
import { TickTestComponent } from './tick-test.component';
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;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should be true after 10s', fakeAsync(() => {
tick(10_001);
component.submit();
expect(callback).toHaveBeenCalled();
}));
});
最小复制回购
解决方案
解决方案
- 进入
fixture.detectChanges()
每个测试,然后打电话给tick(10_000)
那里。 - 移动
timer(10_000)...
到ngOnInit
组件中
发生了什么事
每当您使用fakeAsync
您的代码可以在其中运行的“区域”时。根据我的观察,这个区域“存在”直到超出范围。通过fakeAsync
在 中使用beforeEach
,您会破坏该区域,并且您会遇到计时器未完成的问题(尽管计时器未完成是理想的结果)。
您想将其timer
移入,ngOnInit
因为它不会在调用时立即.createComponent
调用。而是fixture.detectChanges()
在您第一次运行时调用它。因此,当您第一次fixture.detectChanges()
在测试fakeAsync
区域内调用时,ngOnInit
会为您调用,计时器被捕获在区域中,您可以按预期控制时间。
代码
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();
}));
});
推荐阅读
- php - 在 Twilio 中来回短信回复
- excel - Excel - 将多列单元格合并为一个单元格
- arrays - 角度过滤器和合并对象数组
- python - 如何从类别名中获取完全限定的类名
- docker - 具有多个 LoadBalancer 服务的 Kubernetes (docker-desktop)
- asp.net-mvc - 从按钮或下拉列表更改多次提交到不同的 MVC 控制器操作
- r - 如何编写更改结构和替换值的函数?
- javascript - 如果内容没有改变,如何防止 $.POST?
- ios - “仅一次”视图控制器
- spring - 处理 org.hibernate.PropertyAccessException 中文本数组类型列的 postgres 中的 Null 值