web-dev-qa-db-ja.com

Chaiを非同期Mochaテストで動作させる方法はありますか?

ブラウザランナーを使用してMochaで非同期テストを実行しており、Chaiの予想スタイルアサーションを使用しようとしています。

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)

したがって、明らかにエラーをキャッチしており、正しく表示されていないだけです。これを行う方法はありますか?エラーオブジェクトで "done"を呼び出すことはできますが、Chaiなどの優雅さをすべて失い、非常に不格好になります...

79
Thomas Parslow

非同期テストは、失敗したexpect() ationsで、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 );
  }
}
_
93
Jean Vincent

ES6/ES2015の約束と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')
  }))
})
19

約束が好きなら、 Chai as Promised + [〜#〜] q [〜#〜] を試してみてください。

doSomethingAsync().should.eventually.equal("foo").notify(done);
13
xinthink

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

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

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

メーリングリストのディスカッションへのリンク です。

2
DjebbZ

この問題を解決するパッケージを公開しました。

最初に_check-chai_パッケージをインストールします。

_npm install --save check-chai_

次に、テストでchai.use(checkChai);を使用し、次に示すように_chai.check_ヘルパー関数を使用します。

_var chai = require('chai');
var dirtyChai = require('dirty-chai');
var checkChai = require('check-chai');
var expect = chai.expect;
chai.use(dirtyChai);
chai.use(checkChai);

describe('test', function() {

  it('should do something', function(done) {

    // imagine you have some API call here
    // and it returns (err, res, body)
    var err = null;
    var res = {};
    var body = {};

    chai.check(done, function() {
      expect(err).to.be.a('null');
      expect(res).to.be.an('object');
      expect(body).to.be.an('object');
    });

  });

});
_

Per Chaiを非同期Mochaテストで動作させる方法はありますか? これをNPMパッケージとして公開しました。

詳細については、 https://github.com/niftylettuce/check-chai をご覧ください。

1
niftylettuce

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

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

確認可能 、Mocha + Chaiで非常にうまく機能します。

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

1
Manil

Jean Vincent's answer に非常に関連し、それに触発されて、彼のcheck関数に似たヘルパー関数を使用しますが、代わりにeventuallyと呼びます(これはマッチングに役立ちます) chai-as-promisedの命名規則に従ってください)。任意の数の引数を取り、それらを元のコールバックに渡す関数を返します。これにより、テストで余分にネストされた関数ブロックを排除でき、あらゆるタイプの非同期コールバックを処理できます。 ES2015で書かれています。

function eventually(done, fn) {
  return (...args) => {
    try {
      fn(...args);
      done();
    } catch (err) {
      done(err);
    }
  };
};

使用例:

describe("my async test", function() {
  it("should fail", function(done) {
    setTimeout(eventually(done, (param1, param2) => {
      assert.equal(param1, "foo");   // this should pass
      assert.equal(param2, "bogus"); // this should fail
    }), 100, "foo", "bar");
  });
});
1
Ryan McGeary

私はこれを解決するための多くの繰り返しの答えと提案されたパッケージがあることを知っていますが、上記の簡単な解決策が2つのユースケースの簡潔なパターンを提供するのを見たことはありません。私はこれをコピー&ペーストしたい他の人のための統合された回答として投稿しています:

イベントコールバック

function expectEventCallback(done, fn) {
  return function() {
    try { fn(...arguments); }
    catch(error) { return done(error); }
    done();
  };
}

ノードスタイルのコールバック

function expectNodeCallback(done, fn) {
  return function(err, ...args) {
    if (err) { return done(err); }
    try { fn(...args); }
    catch(error) { return done(error); }
    done();
  };
}

使用例

it('handles event callbacks', function(done) {
  something.on('event', expectEventCallback(done, (payload) => {
    expect(payload).to.have.propertry('foo');
  }));
});

it('handles node callbacks', function(done) {
  doSomething(expectNodeCallback(done, (payload) => {
    expect(payload).to.have.propertry('foo');
  }));
});
1
Sukima

_try/catch_を関数に抽出して解決しました。

_function asyncExpect(test, done){
    try{
        test();
        done();
    } catch(error){
        done(error);
    }
}
_

次に、it()で以下を呼び出します。

_it('shall update a Host', function (done) {
            testee.insertHost({_id: 'Host_id'})
                .then(response => {
                    asyncExpect(() => {
                        expect(response).to.have.property('ok', 1);
                        expect(response).to.have.property('nModified', 1);
                    }, done);
                });

        });
_

デバッグも可能です。

0
Amio.io

@richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/ によって提供されるこのリンクに基づいて、describeは使用できますdoneパラメータを省略すると、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));
});
0
Pedro R.

テストおよび非同期のタイマーはかなり荒いようです。約束に基づくアプローチでこれを行う方法があります。

const sendFormResp = async (obj) => {
    const result = await web.chat.postMessage({
        text: 'Hello world!',
    });
   return result
}

この非同期関数はWebクライアントを使用します(この場合はSlacks SDKです)。 SDKは、API呼び出しの非同期の性質を処理し、ペイロードを返します。その後、非同期プロミスで返されたオブジェクトに対してexpectを実行することにより、chai内でペイロードをテストできます。

describe("Slack Logic For Working Demo Environment", function (done) {
    it("Should return an object", () => {
        return sdkLogic.sendFormResp(testModels.workingModel).then(res => {
            expect(res).to.be.a("Object");
        })
    })
});
0
Justin Rice