[英]Angular Unit Test - What is the difference between new Service() and Testbed.inject()?
[英]Angular 2 Unit Test (TestBed) service with .asObservable()
我有一個調用服務的組件,以查看是否已從另一個組件宣布訂閱。
零件:
this.activateProcessReadySubscription = this.returnService.processReadySubscriptionAnnouced$.subscribe(
itemsInCart => {
this.itemsInCart = itemsInCart;
});
當我嘗試對此進行測試時,出現錯誤:
TypeError:無法讀取未定義的屬性“ subscribe”
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);
}
我似乎無法弄清楚如何使訂閱正確測試。
(最后這是非常基本的東西,但是花了我幾天時間才弄清楚……希望能幫助那里的人節省一些時間:)……)
我遇到了同樣的問題,解決該問題的唯一方法是使用吸氣劑,以便能夠在測試時返回模擬值...
因此,在服務中,您必須將屬性更改為吸氣劑:
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)
});
});
***注意:笑話API非常類似於茉莉花...
// jest: jasmine:
// createSpyObj <=> jasmine.createSpyObj
// .mockReturnValue() <=> .and.returnValue()
不要忘記僅導入'of'函數,以在模擬服務中返回可觀察對象...
import { of } from "rxjs/observable/of";
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.