Angular 1.5 && Async/Await && jasmine tests

心已入冬 提交于 2020-01-02 07:58:52

问题


I already looked everywhere but could not find a solution yet for my particular case.

We are using angular 1.5 and a Karma/Jasmine setup for unit tests. In the initial source code, I used ES2017 async/await in the controller. That seemed to work fine as long as I added $apply of $digest manually at the end. So for example:

async function loadData() {
  try {
    vm.isLoading = true;
    vm.data = await DataService.getData();
    $scope.$apply();
  }
  catch (ex) {
    vm.isLoading = false;
  }
}

To write an automated test for this particular function, I tried to mock DataService.getData with Jasmine's spyOn. So, I did something like this:

spyOn(DataService, 'getData').and.returnValue($q.when(fakeResult));

Adding the spy worked, but when running a test, the code seems to get struck and not resolve with fakeResult. I tried adding $digest/$apply in the tests itself but could not fix it. I also did a lot of research, but still have no clue.

Does somebody have a clue?

Edit: testing the same method with $q promises works fine, but I would really like to use async/await...


回答1:


I don't know if your setup is similar, but in our environment I had to do a couple of things to get transpiled async/await statements to resolve in jasmine tests.

  • In our tests, I was trying to mock out services that would return promises. I found that returning $q.when didn't work. Instead, I had to return actual A+ standard Promises. My guess is that Angular's $q promises aren't fully complaint with the standard and wouldn't work as a substitute.

  • Note that since we use PhantomJS for our tests, I had to add polyfills to get those Promises.

  • Many times in my tests I would have to wrap some of the expect statements in a setTimeout block. Again, my assumption is that the promises need an additional "tick" to process.




回答2:


So the reason why there is no result is because nothing after await is executed. I had a similar issue when I was testing async/await inside react components. I found somewhere that a testing method for this is as follows:

  1. You have to use something similar to https://www.npmjs.com/package/jasmine-async-suite - this is what I used. It is for asynchronous tests where you are expecting a promise.
    • There could be also an other problem, that after the promise is resolved, the test stops, because there is nothing to wait for :). It could be very tricky.
  2. So you have to manually call the method, in your case DataService.getData(), and use the .then() method, where you can put your expect statements - because of this step, your test is waiting for resolving the promise.

Here is an example of my code (I am also using async functions in tests):

it.async('should call `mySpecialMethod`', async () => {
    const arrayToResolve = [ { data: 'data' } ];
    const SomeService = context.SomeService;

    spyOn(props, 'mySpecialMethod');
    spyOn(SomeService, 'myMethodReturningPromise');
      .and.returnValue(Promise.resolve(arrayToResolve));

    //the method `myMethodReturningPromise` is called in componentDidMount
    const wrapper = mount(<MyReactComponent {...props} />, context);
    expect(SomeService.myMethodReturningPromise).toHaveBeenCalled();

    //now I am calling the method again
    await SomeService.myMethodReturningPromise();

    //`mySpecialMethod` is calling after the `await` in my code
    expect(props.mySpecialMethod).toHaveBeenCalled();
    //after that I am changing the state
    expect(wrapper.state().arrayToResolve).toEqual(arrayToResolve);
});

I hope this helps you :)




回答3:


You can use the library async-await-jasmine:

import * as angular from 'angular';
import 'angular-mocks';
import {yourModule} from "./your-module-path";
import {LoadDataService} from './load-data';
import {$it} from "async-await-jasmine";
import {IRootScopeService} from "angular";


describe('Calculator', () => {
  let $httpBackend: angular.IHttpBackendService;

  beforeEach(() => {
    angular.mock.module(calculatorModule.name);
    angular.mock.inject((_$httpBackend_) => {
      $httpBackend = _$httpBackend_;
    });
  });

  $it('should loadData', async () => {
    $httpBackend.when('GET', '/loadData').respond('{"value": 5}');
    let sum = loadData(1, 4);
    $httpBackend.flush();

    expect(await sum).toBe(10);
  });
});


来源:https://stackoverflow.com/questions/41028434/angular-1-5-async-await-jasmine-tests

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!