Angular中的单元测试:用Jasmine模拟RxJS



我正在对Angular 12组件进行单元测试。组件在初始化时获取从服务返回的可观察结果(请参阅下面的thing.service.ts(。它被分配给一个Subject,该Subject通过async管道显示在html模板中(请参见下面的app.component.html(。

AppComponent.ts

export class AppComponent  {
public errorObjectSubject = null;
public thingsSubject: Subject<Array<IThing>> = new Subject();
constructor(private readonly _service: ThingService) {
this._getAllProducts();
}
private async _getAllProducts(): Promise<void> {
this._service.getAllObservableAsync()
.pipe(take(1),
catchError(err => {
this.errorObjectSubject = err;
return throwError(err);
})
).subscribe(result => { this.thingsSubject.next(result) });
}
}

模板使用async管道订阅public thingsSubject: Subject<Array<IThing>> = new Subject();

app.component.html

<div>
<app-thing *ngFor="let thing of thingsSubject | async" [thing]="thing"></app-thing>
</div>

thing.service.ts

constructor(private readonly _http: HttpClient) { }
public getAllObservableAsync(): Observable<Array<IThing>> {
return this._http.get<Array<IThing>>('https://jsonplaceholder.typicode.com/todos'); }

这是测试设置。

应用程序组件规范

describe('AppComponent', () => {
let component: AppComponent,
fixture: ComponentFixture<AppComponent>,
dependencies: { thingService: ThingServiceStub };
function getThings(): Array<DebugElement> {
return fixture.debugElement.queryAll(By.directive(ThingComponentStub));
}
beforeEach(async () => {
dependencies = {
thingService: new ThingServiceStub()
};
await TestBed.configureTestingModule({
declarations: [AppComponent, ThingComponentStub],
providers: [
{ provide: ThingService, useValue: dependencies.thingService }
]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
//fixture.detectChanges();
});
describe('on initialisation', () => {
let getThingsSubject: Subject<Array<IThing>>;
beforeEach(() => {
getThingsSubject = new Subject();
(dependencies.thingService
.getAllObservableAsync as jasmine.Spy).and.returnValue(
getThingsSubject.asObservable()
);
fixture.detectChanges();
});
it('should fetch all of the things', () => {
//fixture.detectChanges();
expect(
dependencies.thingService.getAllObservableAsync
).toHaveBeenCalledWith();
});
describe('when the things have been fetched', () => {
beforeEach(fakeAsync(() => {
getThingsSubject.next()
// getThingsSubject.next([
//   {
//     userId: 1,
//     id: 1,
//     title: 'string',
//     completed: 'string'
//   }
// ]);
//getThingsSubject.pipe().subscribe()
tick();
fixture.detectChanges();
}));
it('should display the things', () => {
expect(getThings()[0].componentInstance.product).toEqual({
name: 'product',
number: '1'
});
});
});
});
});

thing.service.stub.ts

export class ProductServiceStub {
public getAllObservableAsync: jasmine.Spy = jasmine.createSpy('getAllObservableAsync');
}

我正在尝试在模板中填充内容(IThing[](之后测试它是如何工作的。我有一个通过的规范,它调用mock observable:

it('should fetch all of the things', () => {
expect(
dependencies.thingService.getAllObservableAsync
).toHaveBeenCalledWith();
});

然而,当我尝试测试模板describe('when the things have been fetched'时,我遇到了"错误:未捕获(承诺中(:类型错误:无法读取未定义的属性"pipe"。

我不太确定问题出在哪里。这是我设置主题订阅的方式吗?还是变化检测?

我认为调用事物的顺序可能是个问题。

试试这个:

describe('AppComponent', () => {
let component: AppComponent,
fixture: ComponentFixture<AppComponent>,
dependencies: { thingService: ThingServiceStub };

function getThings(): Array<DebugElement> {
return fixture.debugElement.queryAll(By.directive(ThingComponentStub));
}
beforeEach(async () => {
dependencies = {
thingService: new ThingServiceStub()
};
await TestBed.configureTestingModule({
declarations: [AppComponent, ThingComponentStub],
providers: [
{ provide: ThingService, useValue: dependencies.thingService }
]
}).compileComponents();
});
beforeEach(() => {
// !! This (.createComponent) is when the constructor is called, so mock the observable
// before here and change the subject to a BehaviorSubject.
// Maybe subject will work as well.
let getThingsSubject = new BehaviorSubject([{ name: 'product', number: '1' }]);
(dependencies.thingService.getAllObservableAsync as jasmine.Spy).and.returnValue(
getThingsSubject.asObservable()
);
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
});
describe('on initialisation', () => {
let getThingsSubject: Subject<Array<IThing>>;
it('should fetch all of the things', () => {
expect(
dependencies.thingService.getAllObservableAsync
).toHaveBeenCalledWith();
});
describe('when the things have been fetched', () => {
// maybe tick and fakeAsync are not needed but `fixture.detectChanges` is
beforeEach(fakeAsync(() => {
tick();
fixture.detectChanges();
}));
it('should display the things', () => {
// !! Add this log for debugging
console.log(fixture.nativeElement);
expect(getThings()[0].componentInstance.product).toEqual({
name: 'product',
number: '1'
});
});
});
});
});

最新更新