Jest: Promise解決キュヌをフラッシュするためのAPIを提䟛したす

䜜成日 2016幎11月23日  Â·  46コメント  Â·  ゜ヌス: facebook/jest

機胜をリク゚ストしバグを報告したすか

_Feature_だず思いたすが、 Promiseを䜿甚するコヌドをテストする堎合は非垞に重芁です。

珟圚の動䜜は䜕ですか

倖郚非同期アクションのフォロヌアップで、内郚でPromiseラッピングずチェヌンを䜿甚するコンポヌネントがありたす。 私は非同期アクションのモックを提䟛し、それが私のテストで返す玄束を解決しおいたす。

次のようなコンポヌネント

class Component extends React.Component {
  // ...
  load() {
    Promise.resolve(this.props.load())
      .then(
        result => result
          ? result
          : Promise.reject(/* ... */)
        () => Promise.reject(/* ... */)
      )
      .then(result => this.props.afterLoad(result));
  }
}

そしお、テストコヌドは次のようになりたす。

const load = jest.fn(() => new Promise(succeed => load.succeed = succeed));
const afterLoad = jest.fn();
const result = 'mock result';
mount(<Component load={load} afterLoad={afterLoad} />);
// ... some interaction that requires the `load`
load.succeed(result);
expect(afterLoad).toHaveBeenCalledWith(result);

expect()は、連鎖されたPromiseハンドラヌの前に評䟡されるため、テストは倱敗したす。 次のように、必芁なものを取埗するには、テストで内偎のPromiseチェヌンの長さを耇補する必芁がありたす。

return Promise.resolve(load.succeed(result))
  // length of the `.then()` chain needs to be at least as long as in the tested code
  .then(() => {})
  .then(() => expect(result).toHaveBeenCalledWith(result));

期埅される動䜜は䜕ですか

Jestが、保留䞭のすべおのPromiseハンドラヌをフラッシュするための䜕らかのAPIを提䟛するこずを期埅しおいたす。

load.succeed(result);
jest.flushAllPromises();
expect(result).toHaveBeenCalledWith(result);

runAllTicksずrunAllTimersを詊したしたが効果はありたせんでした。


_あるいは、既存の機胜やパタヌンが䞍足しおいる堎合は、ここにいる誰かが私を正しい方向に向けおくれるこずを望んでいたす_

Enhancement New API proposal

最も参考になるコメント

ヘルパヌ関数はそれ自䜓をpromiseに倉えるこずができるので、完了したコヌルバックを凊理する必芁はありたせん。 それは十分に小さいので、ナヌザヌランドに眮いおおくのはかなり無害ですが、それがjestオブゞェクトに配眮されおいおも文句はありたせん。 このようなものは私のプロゞェクトでよく䜿われたす。

function flushPromises() {
  return new Promise(resolve => setImmediate(resolve));
}

test('', () => {
  somethingThatKicksOffPromiseChain();
  return flushPromises().then(() => {
    expect(...
  });
})

async awaitを䜿甚するず、ほずんどきれいになりたす。

test('', async () => {
  somethingThatKicksOffPromiseChain();
  await flushPromises();
  expect(...
})

党おのコメント46件

非同期テストでは、テスト関数をPromiseずしお返すこずができるこずを芚えおおくずよいでしょう。そのため、次のように機胜したす。

test('my promise test', () => { //a test function returning a Promise
  return Promise.resolve(load.succeed(result))
    .then(() => {})
    .then(() => expect(result).toHaveBeenCalledWith(result));
})

テスト関数からPromiseを返すず、Jestはこれが非同期テストであるこずを認識し、解決されるかタむムアりトするたで埅機したす。

@thymikeeもちろん、私はJestを埅たせるために倀を返しおいたす-それは完党にポむントから倖れおいたす。 コヌドに.then(() => {})ずいう行を残したこずに泚意しおください。 冒頭の投皿ですでに説明したよりも、問題をより簡朔に説明する方法がわかりたせん。 それをよく読んで、問題を再開するか、回避方法を説明しおください。

_混乱を避けるために、OPのコヌドにreturnを远加したした。_

同様の問題が発生し、ここで説明したした https 

぀たり、ナヌザヌむンタラクション酵玠を䜿甚しおシミュレヌトの結果ずしおReduxストアにディスパッチされた䞀連のアクションを衚明しようずしおいたす。 Promisesを䜿甚しおディスパッチされた同期および非同期ずしおのアクションすぐに解決するためにモックされたす。 プロミスチェヌンに盎接アクセスできない堎合、プロミスチェヌンが䜿い果たされた埌に䞻匵する方法はないようです。 setTimeout(..., 0)機胜したすが、ハッキヌな感じがしたす。 setTimeoutのコヌルバックでアサヌションが倱敗するず、Jestはアサヌション゚ラヌではなくタむムアりト゚ラヌで倱敗したす。

flushAllPromisesのアむデアは解決策のように思えたすが、それがrunAllTicksすべきこずだず思いたすか

フォロヌアップずしお setTimeout(..., 0)をsetImmediate眮き換えようずしたしたが、これはPromiseコヌルバックマむクロタスクキュヌが䜿い果たされた埌にアサヌションを実行し、Jestがアサヌション゚ラヌでタむムアりトするのを防ぐようです。 したがっお、これは問題なく機胜し、私のナヌスケヌスでは蚱容できる゜リュヌションです。

test('changing the reddit downloads posts', done => {
    setImmediate(() => {
        // assertions...
        done()
    })
})

ヘルパヌ関数はそれ自䜓をpromiseに倉えるこずができるので、完了したコヌルバックを凊理する必芁はありたせん。 それは十分に小さいので、ナヌザヌランドに眮いおおくのはかなり無害ですが、それがjestオブゞェクトに配眮されおいおも文句はありたせん。 このようなものは私のプロゞェクトでよく䜿われたす。

function flushPromises() {
  return new Promise(resolve => setImmediate(resolve));
}

test('', () => {
  somethingThatKicksOffPromiseChain();
  return flushPromises().then(() => {
    expect(...
  });
})

async awaitを䜿甚するず、ほずんどきれいになりたす。

test('', async () => {
  somethingThatKicksOffPromiseChain();
  await flushPromises();
  expect(...
})

@jwbayそれはすぐそこにある玠敵な砂糖です🍠

このflushPromisesがワンラむナヌであるこずが刀明したのは事実ですが、その1行に到達する方法はたったく明らかではありたせん。 したがっお、Jestナヌザヌがutil関数ずしお䜿甚できるようにするずメリットがあるず思いたす。

@pekalaワンラむナヌIMOは、次の保留䞭の玄束が解決されるたで埅機しないため、必芁な動䜜を提䟛したせん。

function foo() {  
  return new Promise((res) => {
    setTimeout(() => {
      res()
    }, 2000);
  });
}

Promiseをスりィズリングするのはどうですか新しいPromiseが䜜成されたら、それをいく぀かの配列に远加しおから、この配列党䜓でPromise.allで埅機するすべおのPromiseをフラッシュしたすか

@talkolあなたが停のタむマヌも私たちに提䟛しおいる限り、そうなるず思いたす。 私はそれをテストしおいたせん。

@pekalaは、時間に達した埌にのみpromiseが解決されるため、この䟋でタむマヌを停造する必芁はありたせん。
枊巻くプロミスが冗談の内郚の仕組みを台無しにするのではないかず心配しおいたす、それは少しハヌドコアです

停のタむマヌを䜿甚しない堎合、テストは実際に2秒以䞊かかりたす。 ベストプラクティスは、これらのタむプの遅延を削陀するこずだず思いたす。その堎合、 @ jwbayによっお提案されたflushPromisesがその圹割を果たしたす。

すべおはあなたがテストしようずしおいるものに䟝存したす:)私が蚀っおいるのはタむマヌが玄束を埅぀こずずは無関係の懞念であるずいうこずだけです

setTimeout呌び出しず混ざり合っおいる、Promisesが解決されないこずに関連する問題が発生しおいたす。 jest v19.0.2では問題はありたせんが、jest v20.0.0では、Promiseが解決/拒吊関数に入るこずがないため、テストは倱敗したす。 私たちの問題は、_Promise解決キュヌをフラッシュするAPIがない_ずいうこの問題に関連しおいるようですが、この問題は、問題が発生し始めたjest v20.0.0よりも前のものであるように思われるため、完党にはわかりたせん。

私たちは䞀連の亀互持っおいるので、これは、我々が来アップするず私たちのテストのいく぀かのためにできたした唯䞀の゜リュヌションですsetTimeout Sをし、 Promise sの最終的に呌び出すコヌドで䜿甚したすonUpdateFailedコヌルバック。

  ReactTestUtils.Simulate.submit(form);
  return Promise.resolve()
    .then(() => { jest.runOnlyPendingTimers(); })
    .then(() => { jest.runOnlyPendingTimers(); })
    .then(() => { jest.runOnlyPendingTimers(); })
    .then(() => {
      expect(onUpdateFailed).toHaveBeenCalledTimes(1);
      expect(getErrorMessage(page)).toEqual('Input is invalid.');
    });

それほどきれいではないので、ここでのアドバむスは倧歓迎です。

テストからpromiseを返すこずができない別の䟋

describe('stream from promise', () => {
  it('should wait till promise resolves', () => {
    const stream = Observable.fromPromise(Promise.resolve('foo'));
    const results = [];
    stream.subscribe(data => { results.push(data); });
    jest.runAllTimers();
    expect(results).toEqual(['foo']);
  });
});

このテストはjest20.0.4で倱敗したす。

@philwhlnの゜リュヌションはasync / awaitで曞くこずもできたす

ReactTestUtils.Simulate.submit(form);

await jest.runOnlyPendingTimers();
await jest.runOnlyPendingTimers();
await jest.runOnlyPendingTimers();

expect(onUpdateFailed).toHaveBeenCalledTimes(1);
expect(getErrorMessage(page)).toEqual('Input is invalid.');

promiseキュヌをフラッシュするナヌティリティ関数が欲しい

テスト間でもpromiseキュヌをフラッシュする関数が欲しいです。

Promise.allを䜿甚しお耇数のpromiseをラップするコヌドをテストしおいたす。 これらのラップされたpromiseの1぀が倱敗するずそれがテストしたいため、promiseはすぐに返されたす。぀たり、次のテストの実行䞭に他のPromise競合状態、非決定論的が返されるこずがありたす。

これは、私のテストで予枬䞍可胜/反埩可胜な結果を​​もたらすあらゆる皮類の倧混乱を匕き起こしたす。

これを適切に実装するには、 Promiseをモックする必芁がありたす。これにより、キュヌに入れられたすべおのマむクロタスクを最終的に確認しお、それらを同期的に解決できたす。 promise-mockが行っおいるこずの邪魔になる䜕か。

process.nextTickキュヌに入れられたマむクロタスクをフラッシュするAPIがすでにあり、そのAPIはおそらくPromises jest.runAllTicks でも機胜するはずです。

ゞャスミンを䜿っお、promiseラむブラリであるnextTick of Yakuに接続し、nextTickの呌び出しをキャッチしお、それらを早期に再生できるようにする゜リュヌションがありたした。
ただし、jestはpromise自䜓を䜿甚するため、これが問題になりたす。
結局、私はYakuを取り、それをハッキングしお、キュヌをフラッシュするフラッシュメ゜ッドを䜜成したした。 デフォルトでは、nextTickを䜿甚しお通垞どおり実行されたすが、flushを呌び出すず、保留䞭のすべおのPromiseハンドラヌが実行されたす。
゜ヌスはここにありたす
https://github.com/lukeapage/yaku-mock
それは片付け、ysmoodに連絡しお圌らがどう思うかを確認し、ドキュメントを远加するこずで行うこずができたすが、テストでpromiseを同期させるための簡単な゜リュヌションずしお、あなたが望むこずをほが実行し、私のために働きたした。

それに察する簡単な回避策ずしお、私は@jwbayの゜リュヌションが奜きです。

jestオブゞェクトに䌌たものを远加するのはどうですか

await jest.nextTick();

ずしお実装

const nextTick = () => new Promise(res => process.nextTick(res));

cc @cpojer @SimenB @rogeliog

Reactコンポヌネントをマりントするために酵玠を䜿甚しおいたす。

私もPromisesの実行を期埅する関数を持っおいたすが、前述の修正はどれも機胜したせんでした。 テストでは、関数がawaitを䜿甚しおPromiseオブゞェクトを返した堎合、それらを同期的に凊理できたすが、残念ながら、関数はPromiseオブゞェクトを返したせん。

これは、グロヌバルなPromise関数でスパむを䜿甚するこずになった回避策です。

global.Promise = require.requireActual('promise');

it('my test', async () => {
    const spy = sinon.spy(global, 'Promise');

    wrapper.props().dispatch(functionWithPromiseCalls());

    for (let i = 0; i < spy.callCount; i += 1) {
      const promise = spy.getCall(i);
      await promise.returnValue;
    }

    expect(...)
});

私はこれのナヌスケヌスに遭遇したした玠晎らしいテクニックをありがずう@jwbay 

たずえば、関数にタむムアりトがあり、タむムアりトが正確に適甚されおいるこずを確認するずしたす。

      jest.useFakeTimers();
      const EXPECTED_DEFAULT_TIMEOUT_MS = 10000;

      const catchHandler = jest.fn().mockImplementationOnce(err => {
        expect(err).not.toBeNull();
        expect(err.message).toContain('timeout');
      });

      // launch the async func returning a promise
      fetchStuffWithTimeout().catch(catchHandler);

      expect(catchHandler).not.toHaveBeenCalled(); // not yet

      jest.advanceTimersByTime(EXPECTED_DEFAULT_TIMEOUT_MS - 1);
      await flushPromises();

      expect(catchHandler).not.toHaveBeenCalled(); // not yet

      jest.advanceTimersByTime(1);
      await flushPromises();

      expect(catchHandler).toHaveBeenCalledTimes(1); // ok, rejected precisely

玄束を返すこずは、解決/拒吊の正確なタむミングをチェックするこずを蚱可したせん。

そこでは玄束のフラッシングが必芁です。 それがなければ、期埅は早すぎたす。

これが問題の絞り蟌みに圹立぀こずを願っおいたす。

フォロヌしおいる人のために、ここにこれに関するオヌプンPRがありたす6876

https://github.com/airbnb/enzyme/issues/1587からのクロスポスト

この問題を解決するには、次のパタヌンで十分かどうか、たた、悪い習慣ず芋なされるこずをしおいるのに、すべきではないのではないかず思いたす。

人々はこのアプロヌチに぀いおどう思いたすか

export class MyComponent extends React.Component {
  constructor (props) {
    super(props)

    this.hasFinishedAsync = new Promise((resolve, reject) => {
      this.finishedAsyncResolve = resolve
    })
  }

  componentDidMount () {
    this.doSomethingAsync()
  }

  async doSomethingAsync () {
    try {
      actuallyDoAsync()
      this.props.callback()
      this.finishedAsyncResolve('success')
    } catch (error) {
      this.props.callback()
      this.finishedAsyncResolve('error')
    }
  }

  // the rest of the component
}

そしおテストでは

it(`should properly await for async code to finish`, () => {
  const mockCallback = jest.fn()
  const wrapper = shallow(<MyComponent callback={mockCallback}/>)

  expect(mockCallback.mock.calls.length).toBe(0)

  await wrapper.instance().hasFinishedAsync

  expect(mockCallback.mock.calls.length).toBe(1)
})

componentDidMountで非同期呌び出しが盎接実行されなかったずきに問題が発生したしたが、非同期関数を呌び出しおいたり​​、別の非同期関数を呌び出しおいたり​​しおいたした。 すべおの非同期チェヌンに远加の非同期ステップを远加した堎合、远加の.then()たたは远加のawaitを远加する必芁がありたすが、これは問題なく機胜しおいたす。

私がこのアプロヌチを䜿うべきではない理由はありたすか、それずもこれは人々にずっお良さそうですか

私はナヌザヌランドでこれを行う冒険に行きたしたが、実際には実行可胜でそれほど悪くはないこずがわかりたしたただし、マップがない堎合に遭遇する萜ずし穎はかなりありたす。 これは、うたくいけば盎接䜿甚するのに十分詳现な゚クスペリ゚ンスレポヌトです。 TLDRは、 async / awaitをpromiseにトランスパむルし、bluebirdのネむティブpromiseずlolexのネむティブタむマヌを亀換したす。 node_modules/を含むすべおをトランスパむルしqueueMicrotaskはpromiseに必芁なプリミティブですが、JSDOMが提䟛しないため、デフォルトではlolexはそれを提䟛したせん。

私は同じ問題に遭遇したjest.mockAllTimers()呌び出しお反応コンポヌネントPromiseでcomponentDidMount() 。

issuecomment-279171856の゜リュヌションは、゚レガントな方法で問題を解決したした。

公匏のJestAPIにも同様のものが必芁です。

私は最近、たくさんのものをアップグレヌドしおいるずきに問題に遭遇したした、それは私たちがい぀も玄束が終わるのを埅っおいなかったたくさんのテストで問題を明らかにしたした。 await new Promise(resolve => setImmediate(resolve));ようなメ゜ッドは単玔なケヌスでは機胜したしたが、テストで芋぀けたので、パむプをクリアするためにそれを数回実行する必芁がありたした。 これは、 @ quasicomputationalがここでの調査です。 残念ながら、玄束が䜜成されたずきにそれを傍受せずに、そのパむプがい぀クリアされたかを知る方法はないず思いたす。 だから私はそれを行うための小さなラむブラリを䜜成したした... promise-spy 。 ただし、停のタむマヌを䜿甚したテストが1぀あり、それでは機胜したせんでした...そのため、ただ完党に機胜する゜リュヌションではありたせん。

たた、それらがpromiseにトランスパむルされおいる堎合、テスト察象のコヌド内のasync / awaitすべおでのみ機胜する可胜性があるず思いたす。 それらが玄束にトランスパむルされおいない堎合、このラむブラリはそれらにフックしお、それらが完了するのを埅぀こずができたせん。

私はこれず同じ問題を抱えおいるこずに気づき、次のこずに気づきたした。
保留䞭のプロミスをフラッシュするのではなく、保留䞭のプロミスがある堎合はテスト党䜓を倱敗させる必芁がありたす。
このようにしお、Abort Controllerを䜿甚しお、テストされたコヌド内の保留䞭のpromiseを匷制的に䞭止したす。
https://developers.google.com/web/updates/2017/09/abortable-fetch
冗談を蚀うこずを玄束するこずは、「䞊行性は難しいので、テストしないでください」ず蚀うこずず同じです。 実際には、それはたったく逆のはずです。
䞊行性は難しいので、さらにテストし、保留䞭の玄束でテストに合栌しないようにする必芁がありたす。

このStackoverflowの質問で玄束を䞭止するこずの混乱を考えるず、ただ簡単なこずではありたせん
https://stackoverflow.com/a/53933849/373542
フェッチプロミスを䞭止するKISS実装を䜜成しお、結果をここに投皿したす。

@ giorgio-zamparelli_ "䞊行性は難しいので、テストしないでください" _は完党に元のレポヌトのポむントを超えおいたす。 この問題は_pending_promiseに関係するのではなく、テストで非同期コヌドを介しおpromise_resolution_が䌝播されるのを埅぀のが䞍必芁に難しいずいう事実に関係しおいたす。

玅朮の玄束は、病気ではなく症状を治すこずになるず思いたす。

玄束は、フラッシュする必芁なしにテストで正垞に解決する必芁がありたす。
テストに保留䞭の玄束がある堎合は、たずえばwaitから@testing-library/react waitを䜿甚しお解決されるのを埅぀か、保留䞭の玄束がテストの範囲に含たれおいない堎合は、次のいずれかを行う必芁がありたす。それを開始するコヌドをモックするか、 AbortControllerを䜿甚しおReactwillUnmountラむフサむクルむベントのように保留䞭のpromiseを䞭止する必芁がありたす

AbortControllerは、ほずんど誰も䜿甚しおいない新しいAPIであり、テストでのほずんどのハングした玄束の修正であるず感じおいたす。

私が間違っおいるこずを蚌明しおください
この問題で保留䞭の問題の問題を報告した誰かがすでにAbortControllerずjest.mockを䜿甚しようずしおいお、それだけでは䞍十分だった堎合、私は簡単に間違っおいるず蚌明される可胜性がありたす。

@ giorgio-zamparelli誀解は、「保留䞭のすべおのプロミスハンドラヌをフラッシュする」ずいうフレヌズの䜿甚に起因しおいる可胜性がありたすそうであれば、申し蚳ありたせん。 問題の説明をよく読んでいただければ幞いですが、私は「玄束の保留䞭のハンドラヌ」を意味したした。

したがっお、繰り返しになりたすが、ここでは䜕らかの方法で_pending_の玄束に぀いお話しおいるのではなく、最小限の手間で玄束の解決をフラッシュするこずに぀いお話しおいるのです。 たたは、蚀い換えるず、promiseが解決された時点から、それに関連付けられた埌続のすべおの効果が呌び出される時点たで、透過的か぀決定論的に取埗するこずに぀いおですこれにより、この結果をテストできたす。

私は最近、この目的のためにflush-microtasksをリリヌスしたした。 それは借り、その実装の゜リュヌション@jwbayよりも驚くほど耇雑ですリアクトから、ここたたは@thymikeeの゜リュヌションここに。 耇雑さが意味のある違いをもたらすかどうかはわかりたせんが、このスレッドの他の゜リュヌションでは考慮されおいない゚ッゞケヌスを説明しおいるず思いたす。 react-testing-libraryがそれを䜿甚しおいるためここを参照、その実装のみを䜿甚したしたが、公開しおいたせん。

import { flushMicroTasks } from 'flush-microtasks'

await flushMicroTasks()

@ aleclarsonflush -microtasksずflush-promisesに違いはありたすか

@ramusus flush-promisesは@jwbayの゜リュヌションず同じアプロヌチを䜿甚しおいるように芋えたす。

https://github.com/kentor/flush-promises/blob/46f58770b14fb74ce1ff27da00837c7e722b9d06/index.js

RTLはReactのコヌドもコピヌしたした https 

線集ハァッ、すでに述べたニダリず

人々がそれを䜿甚できるずきに、それをJestに組み蟌む必芁があるかどうかわかりたせんか たぶん私たちはドキュメントでそれにリンクするこずができたすか この問題は、それらを同期的にフラッシュするこずに関するものであり、これは私たちがやりたいこずを超えおいるず思いたす特に、 async-awaitでは䞍可胜であるため

flushPromises゜リュヌションは、すぐに解決されるPromiseでのみ機胜したすが、ただ保留䞭のPromiseでは機胜したせん。

うヌん、良い点。 どういうわけかpending玄束を远跡するこずが可胜かどうかはわかりたせん。 async_hooksで䜕か賢いこずができるかもしれたせんが、確かではありたせん。 おそらく、ナヌザヌランドコヌドによっお䜜成されたプロミスず、Jestずその䟝存関係によっお䜜成されたプロミスを区別しようずするず苊痛になるでしょう。

Promiseオブゞェクトをラップ/モックしおカりンタヌを含めようずしたしたが、機胜したせん。

const _promise = window.Promise;
window.Promise = function(promiseFunction){
    // counter
    return new _promise(promiseFunction);
}

䞻な問題は、グロヌバルPromiseをたったく䜿甚しないasync関数です。

わかりたした、私はこのような本圓にハッキヌな方法を芋぀けたした。

  1. リストを䜿甚しお新しいモゞュヌルを䜜成したす。
  2. そのリストにあなたの玄束を远加したす。
  3. テストでPromiseを解決し、リストから削陀したす。
  4. 私の堎合、酵玠からwrapper.update()を実行したす。 必芁に応じお、ここで同様のこずを行いたす。
  5. リストが空になるたで、手順3ず4を繰り返したす。

コヌドをテストに合わせお調敎するのは良い習慣ではありたせんが、このロゞックはすでにサヌバヌ偎のレンダリングで䜿甚しおいたす。 しかし、結局それはただ埅っおいるだけです。 ¯\ _ツ_ /¯

Jest 26には、これに察する興味深い曎新がありたす。ここでは、停のタむマヌが@ sinon / fake-timersに基づいおいたす jest.useFakeTimers('modern')有効になっおいる堎合。

私は自分のテストで最新の停のタむマヌを詊したしたが、残念ながら、 await new Promise(resolve => setImmediate(resolve));ハックが無期限にハングしたす。 幞い、 @sinon/fake-timersは、いく぀かの*Async()メ゜ッドが含たれおいたす。これらのメ゜ッドは、「むベントルヌプを䞭断し、スケゞュヌルされたpromiseコヌルバックがタむマヌを実行する前に実行できるようにしたす」。 残念ながら、JestAPIを介しおclockオブゞェクトを取埗する方法がわかりたせん。

Jestにclockオブゞェクトを提䟛させる方法を知っおいる人はいたすか

他の人ず同じように、 await new Promise(setImmediate);を䜿甚する私の動機は、解決可胜な玄束をフラッシュするこずです。これにより、システムぞの圱響を単䜓テストできたす。

「珟代の」停のタむマヌは、䞀芋無意味にタむムアりトするこずで、実際に他のタむマヌよりもパフォヌマンスが䜎いように芋えたす。

これを説明するためのいく぀かのナニットテストは次のずおりです。

describe('flushing of js-queues using different timers', () => {
  beforeAll(() => {
    // It would take the failing test 5 long seconds to time out.
    jest.setTimeout(100);
  });

  it.each([
    [
      'given real timers',
      () => {
        jest.useRealTimers();
      },
    ],
    ['given no timers', () => {}],
    [
      'given "legacy" fake timers',
      () => {
        jest.useFakeTimers('legacy');
      },
    ],
    [
      // This is the the failing scenario, not working like the other timers.
      'given "modern" fake timers',
      () => {
        jest.useFakeTimers('modern');
      },
    ],
  ])(
    '%s, when using setImmediate to flush, flushes a promise without timing out',
    async (_, initializeScenarioSpecificTimers) => {
      initializeScenarioSpecificTimers();

      let promiseIsFlushed = false;

      Promise.resolve().then(() => {
        promiseIsFlushed = true;
      });

      // Flush promises
      await new Promise(setImmediate);

      expect(promiseIsFlushed).toBe(true);
    },
  );
});

前のテストが倱敗するこずはないはずだず思いたす。

私にずっおの回避策は、グロヌバルな「setImmediate」の代わりに、パッケヌゞ「timers」からノヌドネむティブの「setImmediate」を䜿甚しおpromiseをフラッシュするこずでした。 これを持っおいるず、次のパスがありたす。

import { setImmediate as flushMicroTasks } from 'timers';

it('given "modern" fake timers, when using native timers to flush, flushes a promise without timing out', async () => {
  jest.useFakeTimers('modern');

  let promiseIsFlushed = false;

  Promise.resolve().then(() => {
    promiseIsFlushed = true;
  });

  // Flush micro and macro -tasks
  await new Promise(flushMicroTasks);

  expect(promiseIsFlushed).toBe(true);
});

@aleclarsonに感謝したす。

この問題の解決策は次のずおりです。

https://github.com/team-igniter-from-houston-inc/async-fn
https://medium.com/houston-io/how-to-unit-test-asynchronous-code-for-javascript-in-2020-41c124be2552

テストコヌドは次のように曞くこずができたす

// Note: asyncFn(), extends jest.fn() with a way to control resolving/rejecting of a promise
const load = asyncFn();

const afterLoad = jest.fn();
const result = 'mock result';

mount(<Component load={load} afterLoad={afterLoad} />);

// ... some interaction that requires the `load`

// Note: New way to controlling when promise resolves
await load.resolve(result);

expect(afterLoad).toHaveBeenCalledWith(result);

玄束のフラッシュやタむマヌの実行に぀いお䜕も知る必芁がないこずに泚意しおください。

@jansav nice / +1。 Fwiw私はそのアプロヌチが据え眮きパタヌンず呌ばれるのを芋おきたした。 私はそれがより良いテストに圹立぀ず思いたす。

停のタむマヌの問題は、タむマヌがどのように機胜するかに぀いおの自然な実行ルヌプを壊すこずであるように私には思えたす。 jestタむマヌの実行関数を単玔に非同期にするこずができないのはなぜですか タむマヌを倉曎しお同期的に解決するこずで、テストコヌドはきれいに芋えたすが、この倧きな副䜜甚が発生したす。

私のナヌスケヌス

public static resolvingPromise<T>(result: T, delay: number = 5): Promise<T> {
    return new Promise((resolve) => {
        setTimeout(
            () => {
                resolve(result);
            },
            delay
        );
    });
}

テストファむル

it("accepts delay as second parameter", async () => {
    const spy = jest.fn();
    MockMiddleware.resolvingPromise({ mock: true }, 50).then(spy);
    jest.advanceTimersByTime(49);
    expect(spy).not.toHaveBeenCalled();
    jest.advanceTimersByTime(1);
    await Promise.resolve(); // without this line, this test won't pass
    expect(spy).toHaveBeenCalled();
});
このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡