【问题标题】:Angular 1.5 && Async/Await && jasmine testsAngular 1.5 && Async/Await && 茉莉花测试
【发布时间】:2017-04-23 01:03:41
【问题描述】:

我已经到处找了,但还没有找到适合我的特殊情况的解决方案。

我们使用 Angular 1.5 和 Karma/Jasmine 设置进行单元测试。在最初的源代码中,我在控制器中使用了 ES2017 async/await。只要我在最后手动添加 $apply 的 $digest 似乎就可以正常工作。 比如:

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

为了为这个特定的函数编写自动化测试,我尝试使用 Jasmine 的 spyOn 模拟 DataService.getData。所以,我做了这样的事情:

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

添加 spy 有效,但在运行测试时,代码似乎被击中并且无法使用 fakeResult 解析。我尝试在测试本身中添加 $digest/$apply 但无法修复它。我也做了很多研究,但仍然没有任何线索。

有人知道吗?

编辑:用$q promises 测试相同的方法效果很好,但我真的很想使用 async/await...

【问题讨论】:

  • 仅供参考,async/await 是 ES2017 的一部分,将于明年发布,而不是 ES7(今年发布)。

标签: javascript angularjs jasmine async-await ecmascript-2017


【解决方案1】:

我不知道您的设置是否类似,但在我们的环境中,我必须做一些事情来获得转译的 async/await 语句以在 jasmine 测试中解析。

  • 在我们的测试中,我试图模拟出会返回承诺的服务。我发现返回$q.when 不起作用。相反,我必须返回实际的 A+ 标准 Promises。我的猜测是 Angular 的 $q 承诺并不完全符合标准,也不能作为替代品。

  • 请注意,由于我们使用 PhantomJS 进行测试,因此我必须添加 polyfill 才能获得这些 Promise。

  • 在我的测试中,很多时候我必须将一些期望语句包装在 setTimeout 块中。同样,我的假设是,promise 需要额外的“tick”来处理。

【讨论】:

    【解决方案2】:

    所以没有结果的原因是因为await之后什么都没有执行。当我在反应组件中测试async/await 时,我遇到了类似的问题。 我在某处发现了一种测试方法如下:

    1. 你必须使用类似于https://www.npmjs.com/package/jasmine-async-suite 的东西——这是我使用的。它适用于您期望得到承诺的异步测试。
      • 还可能存在其他问题,即在 promise 解决后,测试停止,因为没有什么可以等待 :)。这可能非常棘手。
    2. 因此您必须手动调用该方法,在您的情况下为DataService.getData(),并使用.then() 方法,您可以在其中放置您的expect 语句 - 由于这一步,您的测试正在等待解决承诺.

    这是我的代码示例(我也在测试中使用async 函数):

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

    希望对你有帮助:)

    【讨论】:

      【解决方案3】:

      你可以使用库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);
        });
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-30
        • 2023-03-23
        相关资源
        最近更新 更多