Promise.thenをasync/awaitを使って同期させてテストする方法(jest)


問題

Promise.thenは非同期なので.then内で起こる事をテストしようとしても、時系列的にthenの内容が評価される前にテストが走ってしまい結果としてテストが失敗する。

例えばこういうPromiseを内部で返す関数があり、この.then内で何かしらの評価をしたい場合

promise.async.js
export default class PromiseTest{
  fetchData(callback){
    this.fetch().then(({status}) => {
      if(status == "ok"){
        callback()
      }
    })
  }

  fetch(){
    return new Promise((resolve, reject) => {
      resolve({
        status: "ok"
      })
    })
  }
}

コールバックをmockして、呼ばれるかどうか確認したいがテストが先に走り、その後に.thenが呼ばれるためテストが失敗する

promise.async.js
import PromiseTest from "./promise.async"

describe('PromiseTest', () => {
  describe('async/awaitなし', () => {
    it('先にテストが走るので、失敗する', () => {
      const mockCallback = jest.fn()
      const target = new PromiseTest()

      target.fetchData(mockCallback)
      expect(mockCallback).toHaveBeenCalled()
    });
  });
});

解決方法

async/awaitを使う
これによりtarget.fetchData(mockCallback)が終わるまで待ってくれるので、続くテストがその後に実行され、正しく評価する事が可能になる。

promise.sync.js
import PromiseTest from "./promise.async"
import "babel-polyfill"

describe('PromiseTest', () => {
  describe('async/awaitアリ', () => {
    it('成功する', async () => {
      const mockCallback = jest.fn()
      const target = new PromiseTest()

      await target.fetchData(mockCallback)
      expect(mockCallback).toHaveBeenCalled()
    });
  });
});

注意

import "babel-polyfill"
をしないと
ReferenceError: regeneratorRuntime is not defined
が起きる
参照

diff --git a/promise.async.spec.js b/promise.sync.spec.js
index e856584..0661e6b 100644
--- a/promise.async.spec.js
+++ b/promise.sync.spec.js
@@ -1,12 +1,13 @@
 import PromiseTest from "./promise.async"
+import "babel-polyfill"

 describe('PromiseTest', () => {
-  describe('async/awaitなし', () => {
-    it('先にテストが走るので、失敗する', () => {
+  describe('async/awaitアリ', () => {
+    it('成功する', async () => {
       const mockCallback = jest.fn()
       const target = new PromiseTest()

-      target.fetchData(mockCallback)
+      await target.fetchData(mockCallback)
       expect(mockCallback).toHaveBeenCalled()
     });
   });