【TypeScript】【Jest】TypeError: Cannot read property 'catch' of undefined

  • typescript 3.5.1
  • jest 24.8.0
  • typeorm 0.2.18
import faker from 'faker'

import { deleteSchedule } from '~/domain/deleteSchedule'
import { ScheduleRepository } from '~/repository/ScheduleRepository'
import { scheduleFactory } from '~/test/factory/scheduleFactory'
import { DeleteResult } from 'typeorm'

jest.mock('~/repository/ScheduleRepository')

let scheduleRepository: ScheduleRepository
let scheduleID: number

beforeEach(() => {
  ;((ScheduleRepository as any) as jest.Mock<ScheduleRepository>).mockClear()

  scheduleRepository = new ScheduleRepository()

  scheduleID = faker.random.number()
})

describe('deleteSchedule', () => {
  test('正常系', async () => {
    await deleteSchedule({ client, event, scheduleRepository, scheduleID })
  })
})

このようにRepositoryをモックしてテストを実行すると、

TypeError: Cannot read property 'catch' of undefined

とエラーが出る。

動作コードは下のようになっている。

  const schedule = await data.scheduleRepository
    .findOne(data.scheduleID)
    .catch(() => Promise.reject(new Error('findOne failed')))
  if (schedule == null) return Promise.reject(new Error('schedule is null'))

  await data.scheduleRepository
    .delete(data.scheduleID)
    .catch(() => Promise.reject(new Error('delete failed')))

.catch の部分でレシーバがundefinedになっているのが駄目なようだ。

jestjs.io

jest.mock('./sound-player') を呼ぶと、便利な "自動モック" を返してくれます。これは、クラスのコンストラクタおよびすべてのメソッドの呼び出しをスパイするのに使用できます。 この関数は ES6 クラスをモックコンストラクタに置き換え、すべてのメソッドを、常に undefined を返すモック関数に置き換えます。

モックしたクラスのメソッドはundefinedを返す。 Promiseが返らないのでthenやcatchは生えないというわけだ。

モックインスタンスのメソッドでPromise.resolveを返すには mockResolvedValue mockResolvedValueOnce を使う。 (Promise.rejectを返す場合は mockRejectedValue )

beforeEach(() => {
  ;((ScheduleRepository as any) as jest.Mock<ScheduleRepository>).mockClear()

  scheduleRepository = new ScheduleRepository()
  ;((scheduleRepository as any) as jest.Mocked<
    ScheduleRepository
  >).findOne.mockResolvedValueOnce(new Schedule()) // 追加
  ;((scheduleRepository as any) as jest.Mocked<
    ScheduleRepository
  >).delete.mockResolvedValueOnce(new DeleteResult()) // 追加

  scheduleID = faker.random.number()
})

このようにすればテストが通った。