Reputation: 579
I'm trying to run unit test of my component.
component.ts:
async ngOnInit(): Promise<void> {
await this.dataProviderService.getCurrencyCodesList().then(data => {
this.currencyList = data;
});
this.currencyList.sort((a, b) => {
return a.code > b.code ? 1 : -1;
});
// ...
}
Service method used:
async getCurrencyCodesList(): Promise<any[]> {
// ...
const currencyCodes = currencyList.map(data => {
return {code: data.code, currency: data.currency, table: data.table};
});
return currencyCodes;
}
In spec.ts file I tried to create a stub
//...
it('should have currencies in the list', () => {
expect(component.currencyList.length).toBeGreaterThan(1);
});
});
class DataProviderServiceStub {
async getCurrencyCodesList(): Promise<Observable<any[]>> {
return of ([{code: 'PLN', table: 'A'},
{code: 'EUR', table: 'A'}]);
}
}
// then this:
class DataProviderServiceStub {
async getCurrencyCodesList(): Promise<any[]> {
return ([{code: 'PLN', table: 'none'},
{code: 'EUR', table: 'A'}]);
}
}
//also tried return (of)([]), ([{}]) etc.
The problem is I'm getting such Karma error for using .sort on array I'm getting from stub:
Error: Uncaught (in promise): TypeError: this.currencyList.sort is not a function
TypeError: this.currencyList.sort is not a function
It sometimes comes out as error, other time as AfterAll error and sometimes it says everything is fine. What am I doing wrong?
Test result failed: "TypeError: Cannot read property 'length' of undefined"
Upvotes: 0
Views: 1090
Reputation: 41
Once OnInit is called, this block is executed:
this.dataProviderService.getCurrencyCodesList()
Next, then
block is pushed to a microtask queue and scheduled for a call:
.then(data => {
this.currencyList = data;
});
then it proceeds to this.currencyList.sort
Since then
block is still queued and promise is still not resolved, nothing is assigned to currencyList
, hence it contains either undefined
or whatever was assigned to it on initiation. It calls sort
method on undefined
. As undefined
does not have sort
method, it throws error.
Why are you calling then
if you are using async/await? This is how it should be implemented the async/await
way.
async ngOnInit(): Promise<void> {
this.currencyList = await this.dataProviderService.getCurrencyCodesList();
this.currencyList.sort((a, b) => {
return a.code > b.code ? 1 : -1;
});
// ...
}
It basically waits until getCurrencyCodesList()
promise has been resolved and writes response to currencyList
field. Then it proceeds further the regular sync flow.
Since you are using promises, your stubs should return promises as well not observables. In your case, I'd use jasmine spies rather than stubs for testing, example:
const localizationProviderSpy = jasmine.createSpyObj<LocalizationProviderService>('LocalizationProviderService', ['getCurrencyCodesList']);
Add spy to your provider list, you are most likely using TestBed, and then use it in your test cases like this:
it('should have currencies in the list', async () => {
const expected = [{code: 'PLN', table: 'A'}, {code: 'EUR', table: 'A'}];
localizationProviderSpy
.getCurrencyCodesList
.and
.returnValue(Promise.resolve(expected));
await component.ngOnInit();
expect(component.currencyList.length).toBeGreaterThan(1);
expect(component.currencyList).toEqual(expected);
});
Upvotes: 1
Reputation: 680
The getCurrencyCodesList()
method is asynchronous and when you run this.currencyList.sort
, this.currencyList
has not yet received the data
and it is not yet an array and therefore does not have the sort()
function
What you can do is set the Array
type to currencyList
private currencyList: any []
Upvotes: 0