2017-04-18 10 views
2

マイセットアップがchaisinonchai-sinonchai-as-promisedbabelとES6構文を使用しています。モカチャイSinonテストアクセスできない約束/非同期/イベント・エミッタ

私は、次の(減少)コード

// example.js 
'use strict'; 
import EventEmitter from 'events'; 

class Dispatcher extends EventEmitter { 
    send(x) { 
    doSomethingAsync() // promise is NOT returned 
     .then(() => { 
     this.emit('sent'); 
     }) 
     .catch((err) => { 
     this.emit('error', err); 
     }); 
    } 
} 

注持っている:doSomethingAsyncからの約束が返されていませんが。

は、ここで私は私がsendの結果としてdoSomethingAsyncからの約束を返すことができれば、これを行う方法を知っている私の(縮小)テストファイル

let dispatcher; 
let onSent; 
let onError; 

beforeEach(() => { 
    dispatcher = new Dispatcher(); 

    onSent = sinon.stub(); 
    onError= sinon.stub(); 
    dispatcher.on('sent', onSent); 
    dispatcher.on('error', onError); 
}); 

describe('send',() => { 
    it('should emit "error" on sendFn error instead of "sent"',() => { 
    ... set up state for failure ... 
    dispatcher.send(...); 
    ... What do I do here or how do I wrap the following? ... 
    expect(onSent).not.to.have.been.called; 
    expect(onError).to.have.been.called; 
    }); 
}); 

だが、それはそうではないのです(そしてできなくなります決して)ここに。私が持っているのは、「送信済み」または「エラー」イベントのいずれかが最終的に放出されるという知識です。

私の理想的な構文は次のようになります。

expect(onError).to.eventually.have 

しかし、それは動作しません。私はexpectを新しい約束事で包むだけで、エラーのないバージョンを次のように動作させることができます。しかし、私はこれがなぜ機能するのか分かりません。

// This one works for some unknown reason! 
    it('should emit "send" on send success',() => { 
    ... set up state for success ... 
    dispatcher.send(...); 
    return Promise.resolve().then(() => { 
     expect(onSent).to.have.been.called; 
     expect(onError).not.to.have.been.called; 
    });  
    }); 

内側の約束を公開するような方法でコードをリファクタリングすることができれば、これは解決するのが簡単です。私は他の状況で何度も何度もやっています。しかし、私の質問はここにあります非常に具体的にこれを解決する方法正確なパターン;すなわち、私が約束を暴くためにコードをリファクタリングできないときに、特にアクセス不能な約束または非同期コードの副作用をテストする方法。

私が試してみましたが、少なくともテストは

  • 約束に期待を包む期待を呼び出す前に、私はそのすべてのインナーコールバック/約束を完了するために必要な送信機能をトリガすることができるかどうかを確認するだけで、次の
  • 両方の種々の非同期/待つパターンでコールと期待を送信ラップタイムアウト
  • に期待をラップsinon.useFakeTimers
  • を用い
  • 制御時間

ありがとうございます。

EDIT:

[OK]を、ので、これは完全にばかげているが、ここでは両方の解決と約束を拒否するために機能するソリューションがあります:

it('should behave as expected already!', (done) => { 
    ... set up for failure or success as desired 
    dispatcher.send(); 
    process.nextTick(() => { 
    Promise.resolve().then(() => { 
     ... expectations ... 
     done(); 
    }); 
    }); 
}); 

Iが原因(と私は、これがうまくいくと思います私は、スローされたエラーや拒否された約束が現在のティックで直ちに処理され、解決された約束は次のティックで待ち行列に入れられると仮定します。したがって... process.nextTickは、次のティックでこの関数をキューに入れ、すべてのキャッチ/エラーを完了させ、Promise.resolveによって、キューに入れられた約束が実行された後にキューに入れられるようにします。ちなみに、orderまたはnextTickとpromise.resolve()も切り替えることができ、うまく動作します。

NBイベントは、(本当に非同期に放出されている場合など、独自のprocess.nextTickその後、あなたは3レベルの巣を持っている必要があります。どちらの約束-nextTick-約束またはnextTick-約束-nextTick。

私の言葉でそれは厄介である

...まだタイムアウトよりも良いけれども:!テストは、あなたが約束を拒否/解決するためにテストしているコードのブロックを待っていないので、D

答えて

0

は、アサーションは機能しません

あなたの分析はすべて正しいですが、解決するアプローチはアサーションで約束を解決することです。

私はコードにいくつかの変更を加えて動作させました。

私は約束を返すようにdoSomethingAsyncを行ったが

//コードをテストする

(私はここに適切なソリューションだと思います)

import EventEmitter from 'events'; 

const doSomethingAsync = async (x) => { 
    return x; 
}; 

class Dispatcher extends EventEmitter { 
    send(x) { 
    return doSomethingAsync(x) 
     .then((res) => { 
     this.emit('sent'); 
     }) 
     .catch((err) => { 
     this.emit('error', err); 
     }); 
    } 
} 

export default Dispatcher; 

//テスト

オプション1:使用します待つ

import sinon from 'sinon'; 
import { expect } from 'chai'; 
import Dispatcher from '../dispatcher'; 

describe('send',() => { 
    let dispatcher; 
    let onSent; 
    let onError; 

    beforeEach(() => { 
    dispatcher = new Dispatcher(); 
    onSent = sinon.stub(); 
    onError = sinon.stub(); 
    dispatcher.on('sent', onSent); 
    dispatcher.on('error', onError); 
    }); 

    it('Test 1: should emit "error" on sendFn error instead of "sent"', async() => { 
    await dispatcher.send('hello'); 
    expect(onSent.callCount).to.equal(1); 
    expect(onError.callCount).to.equal(0); 
    }); 
}); 

//結果

糸ラン仕様

送信 は✓sendFn誤差に "エラー" を放出する代わりにすべきである(1S)

✨4.18sで完了を通過

1 "送信します"。

オプション2:試験で約束

で、あなたは私がdispatcher.sendでawaitをを使用していたり​​、また、PS

it('Test 2: should emit "error" on sendFn error instead of "sent"',() => { 
    let error = false; 
    dispatcher.send('hello').then(() => { 
    expect(onSent.callCount).to.equal(1); 
    expect(onError.callCount).to.equal(0); 
    }).catch(() => { 
    error = true; 
    }) 
    expect(error).to.equal(false); 
}); 

以下のような約束を使用して主張することができることを見ることができます - もしあなたは約束を返すことを望んでいない、そして、最初のテストケース(待っているものを使用しているもの)は& 2番目のものに失敗します。

関連する問題