Angular 1.5 && Async/Await && 茉莉花测试

Posted

技术标签:

【中文标题】Angular 1.5 && Async/Await && 茉莉花测试【英文标题】:Angular 1.5 && Async/Await && jasmine tests 【发布时间】: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(今年发布)。 【参考方案1】:

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

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

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

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

【讨论】:

【参考方案2】:

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

    你必须使用类似于https://www.npmjs.com/package/jasmine-async-suite 的东西——这是我使用的。它适用于您期望得到承诺的异步测试。 还可能存在其他问题,即在 promise 解决后,测试停止,因为没有什么可以等待 :)。这可能非常棘手。 因此您必须手动调用该方法,在您的情况下为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);
  );
);

【讨论】:

以上是关于Angular 1.5 && Async/Await && 茉莉花测试的主要内容,如果未能解决你的问题,请参考以下文章

Angular 1.5 中的多级嵌入

Angular 1.5+ 组件可选单向绑定

通过属性将数组传递到Angular 1.5中的组件

哪个 Angular 1.5 组件绑定类型更昂贵? '=' 还是'<'?

使用 Angular 1.5 组件的嵌套路由中的相对视图

带有angular1.5组件meteor和ui-router的动态标题