在Angular 9中,我有一个加载程序服务,如下所示。
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
export interface LoaderState {
show: boolean;
}
@Injectable({
providedIn: 'root'
})
export class LoaderService {
private loaderSubject = new Subject<LoaderState>();
loaderState = this.loaderSubject.asObservable();
constructor() { }
show() {
this.loaderSubject.next({show: true} as LoaderState);
}
hide() {
this.loaderSubject.next({show: false} as LoaderState);
}
}
我想测试show()
和hide()
方法。为此,我写了一个规范如下。
it('should show', (done) => {
spyOn(loaderService, 'show').and.callThrough();
loaderService.show();
expect(loaderService.show).toHaveBeenCalled();
loaderService.loaderState.subscribe((state) => {
expect(state).toBe({show: true});
done();
});
});
但当我运行这个时,我得到了以下错误
Chrome 86.0.4240(Windows 10.0.0(LoaderService应显示FAILED错误:超时-异步回调未在5000ms内调用(由jasmine.DEFAULT_Timeout_INTERVAL设置(
在问这个问题之前,我搜索了很多。但似乎找不到正确的解决方案。我是Jasmin单元测试的新手。感谢您的帮助。
编辑:张贴完整的规格文件以供参考。
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoaderComponent } from './loader.component';
import { LoaderService } from './loader.service';
describe('LoaderService', () => {
let component: LoaderComponent;
let fixture: ComponentFixture<LoaderComponent>;
let loaderService: LoaderService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoaderComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
loaderService = TestBed.inject(LoaderService);
});
it('should be create', () => {
expect(loaderService).toBeTruthy();
});
it('should show', (done) => {
spyOn(loaderService, 'show').and.callThrough();
loaderService.show();
expect(loaderService.show).toHaveBeenCalled();
loaderService.loaderState.subscribe((state) => {
expect(state).toBe({show: true});
done();
});
});
});
问题是在实际方法调用之后调用subscribe。因此,当您调用show();
时,该事件没有任何订阅,并且由于done((回调在其中……它从未被调用。
it('should show', (done) => {
service.loaderState.subscribe((state) => { //subscribe first
expect(state).toEqual({show: true}); // change toEqual instead of toBe since you're comparing objects
done();
});
spyOn(service, 'show').and.callThrough();
service.show(); // invoke after
expect(service.show).toHaveBeenCalled();
});
这是我对这种模式的解决方案:
服务类别
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class MyService {
private myServiceEvent = new Subject<any>();
constructor() { }
success(): Observable<any> {
return this.myServiceEvent.asObservable();
}
doSomething(someData: any): void {
// stuff
this.myServiceEvent.next(someData);
}
}
使用服务的组件(可观察的将由调用服务上doSomething
方法的其他组件或服务触发(
import { Component, OnInit } from '@angular/core';
import { MyService } from ./my-service.ts
@Component({
selector: 'app-mycomponent',
templateUrl: './app-mycomponent.component.html',
styleUrls: ['./app-mycomponent.component.scss']
})
export class AppMyComponent implements OnInit {
private test = '';
constructor(
private myService: MyService,
) { }
ngOnInit() {
this.myService.success().subscribe(data => {
test = data;
});
}
}
使用服务的Observable响应测试组件的规范。请注意服务mock的构造以及Subject变量是如何用于触发Observable的
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Observable, Subject } from 'rxjs';
import { AppMyComponent } from './app-my.component';
import { MyService } from ./my-service.ts
describe('AppMyComponent', () => {
let component: AppMyComponent;
let fixture: ComponentFixture<AppMyComponent>;
const myServiceSubject = new Subject<any>();
const myServiceMock = jasmine.createSpyObj('MyService', [], {
success: () => myServiceSubject.asObservable()
});
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
schemas: [ CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
declarations: [ AppMyComponent ],
providers: [
{ provide: MyService, useValue: myServiceMock }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should execute subscription to MyService success event', fakeAsync(() => {
myServiceSubject.next('somedata');
tick();
expect(componenet.test).toEqual('somedata');
}));