我有一个调用服务的组件,以查看是否已从其他组件宣布订阅。
组件:
this.activateProcessReadySubscription = this.returnService.processReadySubscriptionAnnouced$.subscribe(
itemsInCart => {
this.itemsInCart = itemsInCart;
});
当我尝试测试时,我收到一个错误:
TypeError:无法读取属性'订阅'未定义的
SPEC
it('should call constructor', fakeAsync(() => {
mockReturnsService.setResponse(0, true);
tick();
fixture.detectChanges();
expect(mockReturnsService.processReadySubscriptionAnnouced$Spy).toHaveBeenCalledTimes(1);
}));
服务:
private activateProcessReadySubscriptionSource = new Subject<number>();
processReadySubscriptionAnnouced$ = this.activateProcessReadySubscriptionSource.asObservable();
announceProcessReady(itemsInCart: number) {
this.activateProcessReadySubscriptionSource.next(this.returnCartDataLength);
}
我似乎无法弄清楚如何正确测试订阅。
答案 0 :(得分:0)
(最后这是非常基本的内容,但是花了我几天的时间才弄清楚……希望能帮助那里的人节省一些时间:)……)
我遇到了同样的问题,解决该问题的唯一方法是使用吸气剂,以便能够在测试时返回模拟值...
因此,在您的服务中,您必须将属性更改为吸气剂:
private activateProcessReadySubscriptionSource = new Subject<number>();
processReadySubscriptionAnnouced$ () { return this.activateProcessReadySubscriptionSource.asObservable();}
在那之后,您必须修改访问该属性的方式,以便(现在)在组件上执行它。
现在您可以访问.spec.ts文件上的可观察订阅功能...
我现在用代码告诉你我类似的历史记录:
我有:
/* * * * MyData.service.ts * * * */
// TYPING ASUMPTIONS...
// - DI = [Your defined interface]
// - BS = BehaviorSubject
@Injectable()
export class MyDataService {
private searchToggleSource: BS<DI> = new BS<DI>({ search: false });
public currentToggleSearchStatus: Observable<DI> = this.searchToggleSource.asObservable();
}
/* * * * My.component.ts * * * */
@Component({
selector: 'my-component',
template: './my.component.html',
styleUrls: ['./my.component.css'],
})
export class MyComponent implements OnInit, OnDestroy {
private searchToggleSubscription: Subscription;
public search: boolean;
// DataService being the injected, imported service
constructor(dataService: DataService){ }
ngOnInit(){
this.searchToggleSubscription = this.dataService.currentToggleSearchStatus
.subscribe(
({ search }) => {
this.search = search;
});
}
ngOnDestroy(){
this.searchToggleSubscription.unsubscribe();
}
}
/* * * * My.component.spec.ts * * * */
// ASUMPTIONS
// - USING 'jest'
describe('MyComponent', () => {
let fixture: ComponentFixture<MyComponent>;
let mockDataService;
beforeEach(() => {
mockDataService = createSpyObj('DataService', ['currentToggleSearchStatus', ]);
TestBed.configureTestingModule({
declarations: [
MyComponent,
],
providers: [
{ provide: DataService, useValue: mockDataService },
]
});
fixture = TestBed.createComponent(MyComponent);
});
it('should get state from DataService when ngOnInit', () => {
mockDataService
.currentToggleSearchStatus
.mockReturnValue(of({search: true}));
//... to call ngOnInit()
// ****************** THE ERROR **************************
// **** subscribe is not a function...
// **** Since I have no access to a real Observable from
// **** a fake DataService property...
fixture.detectChanges();
// *** SERIOUSLY I SPEND ALMOST 3 DAYS RESEARCHING AND PLAYING
// *** WITH THE CODE AND WAS NOT ABLE TO FIND/CREATE A SOLUTION...
// *** 'TILL LIGHT CAME IN...
// *******************************************************
expect(fixture.componentInstance.search).toBe(false)
});
});
解决方案...使用吸气剂...我将使用注释“-”显示“解决方法” ...
/* * * * MyData.service.ts * * * */
// TYPING ASUMPTIONS...
// - DI = [Your defined interface]
// - BS = BehaviorSubject
@Injectable()
export class MyDataService {
private searchToggleSource: BS<DI> = new BS<DI>({ search: false });
//------- CHANGED ---
// public currentToggleSearchStatus: Observable<DI> = this.searchToggleSource.asObservable();
//------- A GETTER ------ (MUST RETURN THE OBSERVABLE SUBJECT)
public currentToggleSearchStatus(){
return this.searchToggleSource.asObservable();
}
}
/* * * * My.component.ts * * * */
@Component({
selector: 'my-component',
template: './my.component.html',
styleUrls: ['./my.component.css'],
})
export class MyComponent implements OnInit, OnDestroy {
private searchToggleSubscription: Subscription;
public search: boolean;
// DataService being the injected, imported service
constructor(dataService: DataService){ }
ngOnInit(){
//------------ CHANGED -------
//this.searchToggleSubscription = this.dataService.currentToggleSearchStatus
//.subscribe(
// ({ search }) => {
// this.search = search;
// });
//------------ EXECUTE THE SERVICE GETTER -------
this.searchToggleSubscription = this.dataService.currentToggleSearchStatus()
.subscribe(
({search}) => {
this.search = search;
}
);
}
ngOnDestroy(){
this.searchToggleSubscription.unsubscribe();
}
}
/* * * * My.component.spec.ts * * * */
// ASUMPTIONS
// - USING 'jest'
describe('MyComponent', () => {
let fixture: ComponentFixture<MyComponent>;
let mockDataService;
beforeEach(() => {
mockDataService = createSpyObj('DataSharingSearchService', ['showHideSearchBar', 'currentToggleSearchStatus', ]);
TestBed.configureTestingModule({
declarations: [
MyComponent,
],
providers: [
{ provide: DataService, useValue: mockDataService },
]
});
fixture = TestBed.createComponent(MyComponent);
});
it('should get state from DataService when ngOnInit', () => {
mockDataService
.currentToggleSearchStatus
.mockReturnValue(of({search: true}));
//... to call ngOnInit()
// ------------- NO ERROR :3!!! -------------------
fixture.detectChanges();
expect(fixture.componentInstance.search).toBe(false)
});
});
***注意:jest API与茉莉花非常相似...
// jest: jasmine:
// createSpyObj <=> jasmine.createSpyObj
// .mockReturnValue() <=> .and.returnValue()
别忘了仅导入'of'函数,以在模拟服务中返回可观察对象...
import { of } from "rxjs/observable/of";