使用 typescript、karma 和 jasmine 对 RxJS Observable.timer 进行单元测试
Unit Test RxJS Observable.timer using typescript, karma and jasmine
嗨,我对 Angular2、Karma 和 Jasmine 比较陌生。目前我正在使用 Angular 2 RC4 Jasmine 2.4.x
我有一个 Angular 2 服务,它定期调用这样的 http 服务:
getDataFromDb() { return Observable.timer(0, 2000).flatMap(() => {
return this.http.get(this.backendUrl)
.map(this.extractData)
.catch(this.handleError);
});
}
现在我想测试功能。出于测试目的,我刚刚在没有 Observable.timer 的单独函数上测试了 "http.get",方法是:
const mockHttpProvider = {
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
describe('data.service test suite', () => {
var dataFromDbExpected: any;
beforeEachProviders(() => {
return [
DataService,
MockBackend,
BaseRequestOptions,
provide(Http, mockHttpProvider),
];
});
it('http call to obtain data',
inject(
[DataService, MockBackend],
fakeAsync((service: DataService, backend: MockBackend) => {
backend.connections.subscribe((connection: MockConnection) => {
dataFromDbExpected = 'myData';
let mockResponseBody: any = 'myData';
let response = new ResponseOptions({ body: mockResponseBody });
connection.mockRespond(new Response(response));
});
const parsedData$ = service.getDataFromDb()
.subscribe(response => {
console.log(response);
expect(response).toEqual(dataFromDbExpected);
});
})));
});
我显然想用 Observable.timer 测试整个功能。我想有人可能想使用 rxjs 框架中的 TestScheduler,但是我怎么知道只重复定时器函数 x 次呢?我在打字稿上下文中找不到任何使用它的文档。
编辑:我使用的是 rxjs 5 beta 6
编辑:为 Angular 2.0.0 最终版本添加了工作示例:
describe('when getData', () => {
let backend: MockBackend;
let service: MyService;
let fakeData: MyData[];
let response: Response;
let scheduler: TestScheduler;
beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
backend = be;
service = new MyService(http);
fakeData = [{myfake: 'data'}];
let options = new ResponseOptions({ status: 200, body: fakeData });
response = new Response(options);
scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function (initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, scheduler);
});
}));
it('Should do myTest', async(inject([], () => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
scheduler.schedule(() => {
service.getMyData().subscribe(
myData => {
expect(myData.length).toBe(3,
'should have expected ...');
});
}, 2000, null);
scheduler.flush();
})));
});
您需要将 TestScheduler 注入到 beforeEach 部分内的计时器方法中:
beforeEach(function() {
this.scheduler = new TestScheduler();
this.scheduler.maxFrames = 5000; // Define the max timespan of the scheduler
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function(initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, this.scheduler);
});
});
之后你可以完全控制时间 scheduleAbsolute
:
this.scheduler.schedule(() => {
// should have been called once
// You can put your test code here
}, 1999, null);
this.scheduler.schedule(() => {
// should have been called twice
// You can put your test code here
}, 2000, null);
this.scheduler.schedule(() => {
// should have been called three times
// You can put your test code here
}, 4000, null);
this.scheduler.flush();
您需要 scheduler.flush()
来启动 TestScheduler。
编辑:因此,如果您只想测试它 X 次,请尽可能频繁地使用计划函数(并使用正确的绝对时间(以毫秒为单位))。
edit2:我添加了缺少的调度程序启动
edit3:我改变了它所以应该与 RxJs5 一起工作
edit4:添加 maxFrames
设置,因为默认值为 750 毫秒,并且会阻止测试更长的 运行 序列。
我对 TestScheduler()
方法有疑问,因为 schedule()
箭头函数永远不会执行,所以我找到了另一条路径。
Observable.timer
函数只是 return 一个 Observable,所以我从头开始创建了一个以让我完全控制。
首先,为观察者创建一个变量:
let timerObserver: Observer<any>;
现在在 beforeEach()
中创建间谍并让它 return 成为一个 Observable。在 Observable 中,将您的实例保存到计时器:
beforeEach(() => {
spyOn(Observable, 'timer').and.returnValue(Observable.create(
(observer => {
timerObserver = observer;
})
));
});
在测试中,只要触发Observable:
it('Some Test',()=>{
// do stuff if needed
// trigger the fake timer using the Observer reference
timerObserver.next('');
timerObserver.complete();
expect(somethingToHappenAfterTimerCompletes).toHaveBeenCalled();
});
我也为此苦苦挣扎了一段时间。自从提出这个问题以来,框架显然发生了很多变化,我想我的解决方案可能会对有人有所帮助。我的项目使用 rxjs 5、jasmine 2.8 和 angular 5。
在我的组件中,计时器用于每分钟调用服务中的 http-get 函数。我的问题是,当使用 fakeAsync 区域时,(存根)get 函数从未被调用,我收到错误:"Error: 1 periodic timer(s) still in the queue."。
出现此错误是因为计时器一直在计时,并且在测试结束时并未停止。这可以通过在测试末尾添加 "discardPeriodicTasks();" 来解决,这会导致计时器停止。打钩();可以用来假装时间流逝直到下一个电话。我在我的服务中对我的 get-function 使用了一个 spy 来查看它是否有效:
it(
'should call getTickets from service every .. ms as defined in refreshTime',
fakeAsync(() => {
fixture.detectChanges();
tick();
expect(getTicketsSpy).toHaveBeenCalledTimes(1);
// let 2 * refreshtime pass
tick(2 * component.refreshTime);
expect(getTicketsSpy).toHaveBeenCalledTimes(3);
discardPeriodicTasks();
})
);
refreshTime 是我在定时器中使用的参数。我希望这可以防止有人花半天时间来解决这个问题。
您可以使用 fakeAsync()
非常轻松地测试 Observable 计时器。这是一个显示倒数计时器的组件(使用 momentJS 持续时间):
timeout.component.ts
@Component({
selector: 'app-timeout-modal',
templateUrl: './timeout-modal.component.html'
})
export class TimeoutModalComponent implements OnInit {
countdownTimer: Observable<number>;
countdownSubscription: Subscription;
durationLeft = moment.duration(60000); // millis - 60 seconds
ngOnInit() {
this.countdownTimer = Observable.timer(0, 1000);
this.countdownSubscription = this.countdownTimer
.do(() => this.durationLeft.subtract(1, 's'))
.takeWhile(seconds => this.durationLeft.asSeconds() >= 0)
.subscribe(() => {
if (this.durationLeft.asSeconds() === 0) {
this.logout();
}
});
}
}
timeout.component.spec.ts
beforeEach(async(() => {
...
}));
beforeEach(() => {
fixture = TestBed.createComponent(TimeoutModalComponent);
component = fixture.componentInstance;
});
it('should show a count down', fakeAsync(() => {
fixture.detectChanges();
expect(component.durationLeft.asSeconds()).toEqual(60);
tick(1000);
fixture.detectChanges();
expect(component.durationLeft.asSeconds()).toEqual(59);
component.countdownSubscription.unsubscribe();
}));
嗨,我对 Angular2、Karma 和 Jasmine 比较陌生。目前我正在使用 Angular 2 RC4 Jasmine 2.4.x 我有一个 Angular 2 服务,它定期调用这样的 http 服务:
getDataFromDb() { return Observable.timer(0, 2000).flatMap(() => {
return this.http.get(this.backendUrl)
.map(this.extractData)
.catch(this.handleError);
});
}
现在我想测试功能。出于测试目的,我刚刚在没有 Observable.timer 的单独函数上测试了 "http.get",方法是:
const mockHttpProvider = {
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
describe('data.service test suite', () => {
var dataFromDbExpected: any;
beforeEachProviders(() => {
return [
DataService,
MockBackend,
BaseRequestOptions,
provide(Http, mockHttpProvider),
];
});
it('http call to obtain data',
inject(
[DataService, MockBackend],
fakeAsync((service: DataService, backend: MockBackend) => {
backend.connections.subscribe((connection: MockConnection) => {
dataFromDbExpected = 'myData';
let mockResponseBody: any = 'myData';
let response = new ResponseOptions({ body: mockResponseBody });
connection.mockRespond(new Response(response));
});
const parsedData$ = service.getDataFromDb()
.subscribe(response => {
console.log(response);
expect(response).toEqual(dataFromDbExpected);
});
})));
});
我显然想用 Observable.timer 测试整个功能。我想有人可能想使用 rxjs 框架中的 TestScheduler,但是我怎么知道只重复定时器函数 x 次呢?我在打字稿上下文中找不到任何使用它的文档。
编辑:我使用的是 rxjs 5 beta 6
编辑:为 Angular 2.0.0 最终版本添加了工作示例:
describe('when getData', () => {
let backend: MockBackend;
let service: MyService;
let fakeData: MyData[];
let response: Response;
let scheduler: TestScheduler;
beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
backend = be;
service = new MyService(http);
fakeData = [{myfake: 'data'}];
let options = new ResponseOptions({ status: 200, body: fakeData });
response = new Response(options);
scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function (initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, scheduler);
});
}));
it('Should do myTest', async(inject([], () => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
scheduler.schedule(() => {
service.getMyData().subscribe(
myData => {
expect(myData.length).toBe(3,
'should have expected ...');
});
}, 2000, null);
scheduler.flush();
})));
});
您需要将 TestScheduler 注入到 beforeEach 部分内的计时器方法中:
beforeEach(function() {
this.scheduler = new TestScheduler();
this.scheduler.maxFrames = 5000; // Define the max timespan of the scheduler
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function(initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, this.scheduler);
});
});
之后你可以完全控制时间 scheduleAbsolute
:
this.scheduler.schedule(() => {
// should have been called once
// You can put your test code here
}, 1999, null);
this.scheduler.schedule(() => {
// should have been called twice
// You can put your test code here
}, 2000, null);
this.scheduler.schedule(() => {
// should have been called three times
// You can put your test code here
}, 4000, null);
this.scheduler.flush();
您需要 scheduler.flush()
来启动 TestScheduler。
编辑:因此,如果您只想测试它 X 次,请尽可能频繁地使用计划函数(并使用正确的绝对时间(以毫秒为单位))。
edit2:我添加了缺少的调度程序启动
edit3:我改变了它所以应该与 RxJs5 一起工作
edit4:添加 maxFrames
设置,因为默认值为 750 毫秒,并且会阻止测试更长的 运行 序列。
我对 TestScheduler()
方法有疑问,因为 schedule()
箭头函数永远不会执行,所以我找到了另一条路径。
Observable.timer
函数只是 return 一个 Observable,所以我从头开始创建了一个以让我完全控制。
首先,为观察者创建一个变量:
let timerObserver: Observer<any>;
现在在 beforeEach()
中创建间谍并让它 return 成为一个 Observable。在 Observable 中,将您的实例保存到计时器:
beforeEach(() => {
spyOn(Observable, 'timer').and.returnValue(Observable.create(
(observer => {
timerObserver = observer;
})
));
});
在测试中,只要触发Observable:
it('Some Test',()=>{
// do stuff if needed
// trigger the fake timer using the Observer reference
timerObserver.next('');
timerObserver.complete();
expect(somethingToHappenAfterTimerCompletes).toHaveBeenCalled();
});
我也为此苦苦挣扎了一段时间。自从提出这个问题以来,框架显然发生了很多变化,我想我的解决方案可能会对有人有所帮助。我的项目使用 rxjs 5、jasmine 2.8 和 angular 5。
在我的组件中,计时器用于每分钟调用服务中的 http-get 函数。我的问题是,当使用 fakeAsync 区域时,(存根)get 函数从未被调用,我收到错误:"Error: 1 periodic timer(s) still in the queue."。
出现此错误是因为计时器一直在计时,并且在测试结束时并未停止。这可以通过在测试末尾添加 "discardPeriodicTasks();" 来解决,这会导致计时器停止。打钩();可以用来假装时间流逝直到下一个电话。我在我的服务中对我的 get-function 使用了一个 spy 来查看它是否有效:
it(
'should call getTickets from service every .. ms as defined in refreshTime',
fakeAsync(() => {
fixture.detectChanges();
tick();
expect(getTicketsSpy).toHaveBeenCalledTimes(1);
// let 2 * refreshtime pass
tick(2 * component.refreshTime);
expect(getTicketsSpy).toHaveBeenCalledTimes(3);
discardPeriodicTasks();
})
);
refreshTime 是我在定时器中使用的参数。我希望这可以防止有人花半天时间来解决这个问题。
您可以使用 fakeAsync()
非常轻松地测试 Observable 计时器。这是一个显示倒数计时器的组件(使用 momentJS 持续时间):
timeout.component.ts
@Component({
selector: 'app-timeout-modal',
templateUrl: './timeout-modal.component.html'
})
export class TimeoutModalComponent implements OnInit {
countdownTimer: Observable<number>;
countdownSubscription: Subscription;
durationLeft = moment.duration(60000); // millis - 60 seconds
ngOnInit() {
this.countdownTimer = Observable.timer(0, 1000);
this.countdownSubscription = this.countdownTimer
.do(() => this.durationLeft.subtract(1, 's'))
.takeWhile(seconds => this.durationLeft.asSeconds() >= 0)
.subscribe(() => {
if (this.durationLeft.asSeconds() === 0) {
this.logout();
}
});
}
}
timeout.component.spec.ts
beforeEach(async(() => {
...
}));
beforeEach(() => {
fixture = TestBed.createComponent(TimeoutModalComponent);
component = fixture.componentInstance;
});
it('should show a count down', fakeAsync(() => {
fixture.detectChanges();
expect(component.durationLeft.asSeconds()).toEqual(60);
tick(1000);
fixture.detectChanges();
expect(component.durationLeft.asSeconds()).toEqual(59);
component.countdownSubscription.unsubscribe();
}));