简体   繁体   中英

Unit testing angular 11 service stub problem

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"

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);
});

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 []

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM