81

Browser Runnerを使用してMochaでいくつかの非同期テストを実行しています.Chaiのexpectスタイルアサーションを使用しようとしています:

window.expect = chai.expect;
describe('my test', function() {
  it('should do something', function (done) {
    setTimeout(function () {
      expect(true).to.equal(false);
    }, 100);
  }
}

これは、通常の失敗したアサーション メッセージを表示しません。代わりに、次のメッセージが表示されます。

Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :)
    at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11)
    at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8)
    at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10)

したがって、明らかにエラーをキャッチしています。正しく表示されていないだけです。これを行う方法はありますか?エラーオブジェクトで「完了」を呼び出すこともできると思いますが、Chai のような優雅さをすべて失い、非常にぎこちなくなります...

4

14 に答える 14

97

非同期テストは、例外がのスコープ外でスローされるため、失敗しexpect()た場合にキャプチャできない例外を生成します。it()it()

表示されるキャプチャされた例外はprocess.on('uncaughtException')、ノードの下またはwindow.onerror()ブラウザーで使用してキャプチャされます。

この問題を解決するには、最初のパラメーターとして例外を使用しsetTimeout()て呼び出すために、によって呼び出される非同期関数内で例外をキャプチャする必要があります。done()また、成功を示すためにパラメーターなしで呼び出す必要がありdone()ます。そうしないと、テスト関数が完了したことを通知しないため、mocha がタイムアウト エラーを報告します。

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function ( done ) {
    // done() is provided by it() to indicate asynchronous completion
    // call done() with no parameter to indicate that it() is done() and successful
    // or with an error to indicate that it() failed
    setTimeout( function () {
      // Called from the event loop, not it()
      // So only the event loop could capture uncaught exceptions from here
      try {
        expect( true ).to.equal( false );
        done(); // success: call done with no parameter to indicate that it() is done()
      } catch( e ) {
        done( e ); // failure: call done with an error Object to indicate that it() failed
      }
    }, 100 );
    // returns immediately after setting timeout
    // so it() can no longer catch exception happening asynchronously
  }
}

すべてのテストケースでこれを行うのは煩わしく、DRY ではないため、これを行う関数を提供することをお勧めします。この関数を呼び出しましょうcheck():

function check( done, f ) {
  try {
    f();
    done();
  } catch( e ) {
    done( e );
  }
}

これcheck()で、非同期テストを次のように書き直すことができます。

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function( done ) {
    setTimeout( function () {
      check( done, function() {
        expect( true ).to.equal( false );
      } );
    }, 100 );
  }
}
于 2013-03-04T18:07:24.023 に答える
21

これは、ES6/ES2015 promises と ES7/ES2016 async/await の合格テストです。これが、このトピックを研究している人にとって、更新された素晴らしい回答になることを願っています:

import { expect } from 'chai'

describe('Mocha', () => {
  it('works synchronously', () => {
    expect(true).to.equal(true)
  })

  it('works ansyncronously', done => {
    setTimeout(() => {
      expect(true).to.equal(true)
      done()
    }, 4)
  })

  it('throws errors synchronously', () => {
    return true
    throw new Error('it works')
  })

  it('throws errors ansyncronously', done => {
    setTimeout(() => {
      return done()
      done(new Error('it works'))
    }, 4)
  })

  it('uses promises', () => {
    var testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    testPromise.then(result => {
      expect(result).to.equal('Hello')
    }, reason => {
      throw new Error(reason)
    })
  })

  it('uses es7 async/await', async (done) => {
    const testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    try {
      const result = await testPromise
      expect(result).to.equal('Hello')
      done()
    } catch(err) {
      done(err)
    }
  })

  /*
  *  Higher-order function for use with async/await (last test)
  */
  const mochaAsync = fn => {
    return async (done) => {
      try {
        await fn()
        done()
      } catch (err) {
        done(err)
      }
    }
  }

  it('uses a higher order function wrap around async', mochaAsync(async () => {
    const testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    expect(await testPromise).to.equal('Hello')
  }))
})
于 2015-12-19T03:33:53.333 に答える
13

約束したのが好きなら、Chai as Promised + Qを試してみてください。これにより、次のようなことが可能になります。

doSomethingAsync().should.eventually.equal("foo").notify(done);
于 2014-02-10T15:22:35.137 に答える
2

Mochaメーリングリストで同じことを尋ねました。彼らは基本的に私にこれを言った:MochaとChaiで非同期テストを書く:

  • 常にテストを開始するif (err) done(err);
  • 常に でテストを終了しdone()ます。

それは私の問題を解決し、その間のコードの1行も変更しませんでした(特にChaiの期待)。これsetTimoutは、非同期テストを行う方法ではありません。

メーリング リストのディスカッションへのリンクは次のとおりです。

于 2012-08-20T14:50:53.660 に答える
1

chaiAsPromisedをお試しください!優れた名前を付けるだけでなく、次のようなステートメントを使用できます。

expect(asyncToResultingValue()).to.eventually.equal(true)

確認できます。モカ + チャイで非常にうまく機能します。

https://github.com/domenic/chai-as-promised

于 2016-11-15T03:11:30.377 に答える
0

@richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/が提供するこのリンクに基づいて、done を省略した場合、describe は返された Promise を使用できます。パラメータ。

唯一の欠点は、そこに Promise が必要であり、非同期関数ではありません (Promise でラップできます)。ただし、この場合、コードを大幅に削減できます。

最初の funcThatReturnsAPromise 関数または期待のいずれかからの失敗を考慮に入れます。

it('should test Promises', function () { // <= done removed
    return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added
        .then(response => expect(response).to.have.property('ok', 1));
});
于 2016-09-02T09:46:50.003 に答える
0

より簡単なアプローチは、wait-for-expectライブラリを使用することです。

const waitForExpect = require("wait-for-expect")

test("it waits for the number to change", async () => {
  let numberToChange = 10;

  setTimeout(() => {
    numberToChange = 100;
  }, randomTimeout);

  await waitForExpect(() => {
    expect(numberToChange).toEqual(100);
  });
});
于 2021-06-09T07:10:55.607 に答える
-2

ドメインモジュールを使用することもできます。例えば:

var domain = require('domain').create();

domain.run(function()
{
    // place you code here
});

domain.on('error',function(error){
    // do something with error or simply print it
});
于 2016-09-15T09:06:35.873 に答える
-2

私にとって非常にうまく機能したのは、Sinon's LibraryのfakeTimerでした。必要に応じて、テストでタイマーを進めるだけです。

var sinon = require('sinon');
clock = sinon.useFakeTimers();
// Do whatever. 
clock.tick( 30000 ); // Advances the JS clock 30 seconds.

テストがより速く完了するという追加のボーナスがあります。

于 2015-09-07T20:16:06.407 に答える