[英]Jasmine spy callFake not being reached when trying spy on a service method that is called inside ngOnInit
我正在嘗試為使用服務執行 HTTP 請求以從數據庫檢索信息的組件設置一些單元測試。 我對 angular 和單元測試還很陌生,所以請耐心等待。 在我的測試中,我試圖監視名為getClients
的 function(這個 function 本質上是實際執行 HTTP 請求的服務的處理程序)並使用callFake
。
我遇到的問題是getClients
function 沒有被覆蓋,這讓我相信間諜不起作用或者沒有監視我認為的東西。 我可以看出它沒有被調用,因為失敗消息引用了真實getClients
function 中的內容。
測試代碼:
我在這里的理解是,因為我試圖監視的 function 在ngOnInit
function 中,所以我必須先定義間諜,然后實例化組件。 我也試過在其中運行間諜程序, it
也沒有用。
describe('handleID', () => {
beforeEach(waitForAsync (() => {
spyOn(service, 'getClients').and.callFake(() => {
let companyList = [
{
COMPANYDESCRIPTOR: "Hello World Inc.",
COMPANYNAME: "Hello World Inc.",
CUSTOMERID: "abcdef123456",
CUSTOMERKEY: 123456
}
]
component.companySearchService.companies.next(companyList);
return companyList;
});
fixture = TestBed.createComponent(CompanySearchComponent);
component = fixture.componentInstance;
service = component.companySearchService;
fixture.detectChanges();
}));
it("should update component.companyForm.controls['selectedCompany'] to 'Hello World Inc.'", () => {
component.companyForm = component._formBuilder.group({
selectedCompany: ['']
})
component.pathToNameProp = 'COMPANYNAME';
component.pathToIdProp = ['CUSTOMERID', 'CUSTOMERKEY'];
let id = 123456;
component.handleID(id);
expect(component.companyForm.get('selectedCompany')).toBe('Hello World Inc.');
})
})
實際 function :
為了清楚起見,我在下面提供了getClients
function。 dbService
是進行 API 調用的數據庫服務。 makeAnApiCall
返回一個observable
對象, subscribe
只是將數據傳遞給另一個處理程序,該處理程序根據source
確定如何處理數據。
getClients(endpoint, method, options = []) {
this.loading.next(true);
this.dbService
.makeAnApiCall(endpoint, method, options)
.subscribe(
res => this.generateCompanyList(res, this.source.getValue())
)
}
失敗信息:
失敗消息引用了從數據庫服務的makeAnApiCall
方法返回的可見訂閱。 這讓我相信間諜根本沒有被創造出來,或者完全在監視其他東西。
Failed: Cannot read properties of undefined (reading 'subscribe')
at CompanySearchService.getClients (http://localhost:9876/_karma_webpack_/main.js:6343:13)
at CompanySearchComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/utilities/company-search/company-search.component.ts:98:39)
...
問題:
在此先感謝您提供的所有幫助!
據我所知,您缺少在創建組件之前執行TestBed.configureTestingModule
的操作。 這需要聲明被測組件並提供類似於普通 ngModule 的服務。 最好查看Angular 測試指南以了解如何使用它。
在測試模塊中,您將在 providers 數組中設置 CompanySearchService,然后您可以使用TestBed.inject(CompanySearchService)
訪問它並模擬您想要的方法。
這個解決方案解決了我的問題,但它仍然沒有真正回答我的問題。 因此,如果有人可以對此提供更多說明,我很樂意將他們的回復標記為已接受的答案。
單元測試
看起來我調用spyOn
、 fixture.detectChanges
和component.ngOnInit
的順序擾亂了服務,因此我的Cannot read properties of undefined
錯誤。 下面更新的代碼是完整的單元測試。 我創建了__beforeEach
,所以我不必在嵌套單元測試中重復所有內容。 我最終也完全取消了調用component.ngOnInit()
,因為它似乎detectChanges
也能正常工作。
describe('CompanySearchComponent', () => {
let fixture, component, service;
let __beforeEach = () => {
fixture = TestBed.createComponent(CompanySearchComponent);
component = fixture.componentInstance;
service = component.companySearchService;
component.companySource = 'database';
spyOn(service, 'getClients').and.callFake(() => {
let response = { data: { rows:[
{
COMPANYDESCRIPTOR: "Hello World Inc.",
COMPANYNAME: "Hello World Inc.",
CUSTOMERID: "abcdef123456",
CUSTOMERKEY: 123456
}
]}}
component.companySearchService.generateCompanyList(response, 'database');
});
fixture.detectChanges();
}
beforeAll(waitForAsync(__beforeEach));
it ('should initialize the component and the service', () => {
expect(component).toBeDefined();
expect(service).toBeDefined();
})
it ('should initalize pathToNameProp to \'COMPANYNAME\' and pathToProp to [\'CUSTOMERID\', \'CUSTOMERKEY\'] with source set to \'database\'', () => {
expect(component.pathToNameProp).toBe('COMPANYNAME');
expect(component.pathToIdProp).toEqual(['CUSTOMERID', 'CUSTOMERKEY']);
})
it ('should update companies array', () => {
expect(component.companies).toEqual([
{
COMPANYDESCRIPTOR: "Hello World Inc.",
COMPANYNAME: "Hello World Inc.",
CUSTOMERID: "abcdef123456",
CUSTOMERKEY: 123456
}
]);
})
describe('handleID', () => {
beforeAll(waitForAsync(__beforeEach));
it("should update selectedCompany form'", () => {
let id = '123456';
component.handleID(id);
expect(component.companyForm.controls['selectedCompany'].value.COMPANYNAME).toBe('Hello World Inc.');
})
})
})
設置源
這可能不相關,但我想提出我遇到的另一個問題。 該組件不是獨立的,其他組件將其作為依賴項導入。 也就是說,必須顯式定義companySearchComponent.companySource
然后重新初始化組件,因為它的值是在構造函數中定義的。
constructor(
private elem: ElementRef,
public companySearchService: CompanySearchService,
private hierarchyService: HierarchyService,
private _formBuilder: FormBuilder
) {
this.companySource = this.elem.nativeElement.dataset.companySrc;
this.prevCompanySrc = this.companySearchService.source.getValue();
}
構造函數引用源的選擇器元素
<company-search [hidden]="!showSearch" id="company-search" data-company-src="database"></company-search>
在companySearchComponent.ngOnInit
中,源值用於定義 http 請求和響應的一些重要屬性。 它也用於對 companySearchService.getClients companySearchService.getClients()
的初始調用(我最初遇到問題的 function)。
ngOnInit() {
switch(this.companySource) {
case 'database':
...
this.pathToNameProp = 'COMPANYNAME';
this.pathToIdProp = ['CUSTOMERID', 'CUSTOMERKEY'];
break;
...
default:
break;
}
if (!this.companySearchService.companies.value || this.prevCompanySrc !== this.companySource) {
this.companySearchService.source.next(this.companySource);
this.companySearchService.getClients(this.endpoint, this.httpMethod, this.httpOptions);
}
...
}
就像我說的,這並不能完全回答我提出的問題,但它是問題的解決方案,所以如果有人發布更全面、更徹底的解決方案,我會很樂意將其標記為已接受的答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.