ProtractorのE2Eテストで「Timed out waiting for asynchronous Angular tasks to finish after 11 seconds」エラーでハマった話

Protractorを使ったE2Eテストの際に「Timed out waiting for asynchronous Angular tasks to finish after 11 seconds」というエラー遭遇したので、その対応策を紹介します。
2018.07.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

どうも!大阪オフィスの西村祐二です。

AngularにはE2EテストのフレームワークとしてProtractorが同梱されています。

それを使ってテストを書くときにハマったところと、その対応策を紹介していこうと思います。

今回、「Timed out waiting for asynchronous Angular tasks to finish after 11 seconds」というエラーに遭遇したので、その対応策を紹介します。

テストシナリオ

ページのpタグの値を取得してテストするという簡単なものです。

テストの目的はルーティングとして、「/」にアクセスすると、未ログインの場合はログインコンポーネントのログインページが表示されることをテストしたかったという意図があります。

環境

Angular CLI: 6.0.8
Node: 8.11.3
OS: darwin x64
Angular: 6.0.9

テストファイル

テスト対象のページ

ただ、pタグが記載されているだけです。「/」にアクセスするとこのコンポーネントが呼ばれるようにルーティングしています。

login.component.html

<p>test</p>

E2Eテストのディレクトリ構成

e2e
├── protractor.conf.js
├── src
│   └── login
│       ├── login.e2e-spec.ts
│       └── login.po.ts
└── tsconfig.e2e.json

login.po.ts

ページオブジェクトを取得する処理を記載しています。

ここでは「/」のときのページを取得するメソッドと、pタグのテキストを返すメソッドを定義しています。

login.po.ts

import { browser, element, by } from 'protractor';

export class LoginPage {
  navigateTo() {
    return browser.get('/');
  }
  getPageTitleText() {
    return element(by.css('p')).getText();
  }
}

login.e2e-spec.ts

ページオブジェクトから取得した値が正しいかどうかのテストを記載しています。

pタグの値が「test」となっていれば、OKというテストです。

login.e2e-spec.ts

import { LoginPage } from './login.po';

describe('toppage', () => {
  let page: LoginPage;
  beforeEach(() => {
    page = new LoginPage();
  });

  it('p_tag value test', () => {
    page.navigateTo();
    expect(page.getPageTitleText()).toEqual('test');
  });
});

Protractorを使ってテスト

下記コマンドで、E2Eテストを実行してみましょう。

$ ng e2e

エラー内容

下記のようなエラーとなりました。タイムアウトしているということで、テストのタイミングをルーティングするまでに待つ処理を挟んでみたりしましたが、どれも違いました。

**************************************************
*                    Failures                    *
**************************************************

1) top page test
  - Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQfor more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
  While waiting for element with locator - Locator: By(css selector, p)

Executed 1 of 1 spec (1 FAILED) in 13 secs.
[17:25:16] I/launcher - 0 instance(s) of WebDriver still running
[17:25:16] I/launcher - chrome #01 failed 1 test(s)
[17:25:16] I/launcher - overall: 1 failed spec(s)
[17:25:16] E/launcher - Process exited with error code 1
An unexpected error occured: undefined

対処法

エラーの原因として、Angularの$http$timeoutを待ってくれるwaitForAngularEnabledがデフォルトでTrueとなっていることが原因でした。 これをFalseにしてやります。 そうすることでエラーが解消します。

login.po.ts 

import { browser, element, by } from 'protractor';

export class LoginPage {
  navigateTo() {
    browser.waitForAngularEnabled(false);
    return browser.get('/');
  }
  getPageTitleText() {
    return element(by.css('p')).getText();
  }
}

再度、テストしてみます。

$ ng e2e

下記のようなログが出力されました。うまくいきましたね。

[17:36:29] I/update - chromedriver: chromedriver_2.40 up to date
(node:99068) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
[17:36:30] I/launcher - Running 1 instances of WebDriver
[17:36:30] I/direct - Using ChromeDriver directly...
Jasmine started

  toppage
    ✓ p_tag value test

Executed 1 of 1 spec SUCCESS in 1 sec.
[17:36:33] I/launcher - 0 instance(s) of WebDriver still running
[17:36:33] I/launcher - chrome #01 passed

本当にテストできているか確認するために、login.e2e-spec.tstoEqualを「testt」に変えて再度テストしてみます。

**************************************************
*                    Failures                    *
**************************************************

1) toppage p_tag value test
  - Expected 'test' to equal 'testt'.

Executed 1 of 1 spec (1 FAILED) in 2 secs.
[17:38:33] I/launcher - 0 instance(s) of WebDriver still running
[17:38:33] I/launcher - chrome #01 failed 1 test(s)
[17:38:33] I/launcher - overall: 1 failed spec(s)
[17:38:33] E/launcher - Process exited with error code 1
An unexpected error occured: undefined

想定どおりにテストが失敗していることがわかりますね。

グローバルに設定を反映させる

余談ですが、protractor.conf.jsに設定を記載することでテストファイル全体にbrowser.waitForAngularEnabled(false);を設定することもできます。

protractor.conf.js 

// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './src/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: require('path').join(__dirname, './tsconfig.e2e.json')
    });
    browser.waitForAngularEnabled(false);
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};

さいごに

いかがだったでしょうか。

ProtractorでE2Eテストを書くときに遭遇した、「Timed out waiting for asynchronous Angular tasks to finish after 11 seconds」というエラーの対処法を紹介しました。

どんどんテストを書いてノウハウを共有していきましょう。

誰かの参考になれば幸いです。