Closed6

reactでcustom-hook with axiosのテストをしたい

tara is oktara is ok

前提

  • 最近参画したプロジェクトで、レガシーになるつつあるuseRequest.tsというuseSWRの車輪の再発明にのようなカスタムフックがある。
    • useRequestはaxiosを使用したget, postといった関数を返す(swrでいうfetcher部分です)
  • そこにローディングの状態を持つことになり、testの勉強もかねてloadingの状態が正しく切り替わることを担保したい

インストール

この2つは最低限使うことになりそう
https://github.com/ctimmerm/axios-mock-adapter

https://testing-library.com/docs/react-testing-library/intro/

tara is oktara is ok

一旦これで行けた

import { renderHook, waitFor } from '@testing-library/react'
import MockAdapter from 'axios-mock-adapter'

import { useRequest } from '@/common/hooks/useRequest'

import axios from '../../src/common/libs/axios'

jest.mock('@auth0/auth0-react', () => ({
  useAuth0: () => ({
    getAccessTokenSilently: jest.fn(() => 'mocked-token'),
  }),
}))

const url = 'https://example.com'
const mock = new MockAdapter(axios)

describe('useRequest', () => {
  const { result } = renderHook(() => useRequest({ url }))
  afterEach(() => {
    mock.reset()
  })
  describe('isLoading', () => {
    test('初期値はfalse', () => {
      expect(result.current.isLoading).toBe(false)
    })
    describe('get', () => {
      it('リクエスト中はtrue', async () => {
        mock.onGet(url).reply(200)
        waitFor(async () => {
          await result.current.get()
          expect(result.current.isLoading).toBe(true)
        })
        expect(result.current.isLoading).toBe(false)
      })
      it('リクエスト後はfalse', async () => {
        mock.onGet(url).reply(200)
        await waitFor(async () => {
          await result.current.get()
        })
        expect(result.current.isLoading).toBe(false)
      })
    })
    describe('update', () => {
      it('リクエスト中はtrue', async () => {
        mock.onPost(url).reply(200)
        waitFor(async () => {
          await result.current.update('post', {})
          expect(result.current.isLoading).toBe(true)
        })
        expect(result.current.isLoading).toBe(false)
      })
      it('リクエスト後はfalse', async () => {
        mock.onPost(url).reply(200)
        await waitFor(async () => {
          await result.current.update('post', {})
        })
        expect(result.current.isLoading).toBe(false)
      })
    })
    describe('remove', () => {
      it('リクエスト中はtrue', async () => {
        mock.onDelete(url).reply(200)
        waitFor(async () => {
          await result.current.remove()
          expect(result.current.isLoading).toBe(true)
        })
        expect(result.current.isLoading).toBe(false)
      })
      it('リクエスト後はfalse', async () => {
        mock.onGet(url).reply(200)
        await waitFor(async () => {
          await result.current.remove()
        })
        expect(result.current.isLoading).toBe(false)
      })
    })
  })
})

tara is oktara is ok

冗長な処理を削除した

import { renderHook, waitFor } from '@testing-library/react'
import MockAdapter from 'axios-mock-adapter'

import { useRequest } from '@/common/hooks/useRequest'

import axios from '../../src/common/libs/axios'

jest.mock('@auth0/auth0-react', () => ({
  useAuth0: () => ({
    getAccessTokenSilently: jest.fn(() => 'mocked-token'),
  }),
}))

const url = 'https://example.com'

describe('useRequest', () => {
  const mock = new MockAdapter(axios)
  const {
    result: {
      current: { isLoading, get, update, remove },
    },
  } = renderHook(() => useRequest({ url }))
  afterEach(() => {
    mock.reset()
  })
  describe('isLoading', () => {
    test('初期値はfalse', () => expect(isLoading).toBe(false))
    describe('get', () => {
      it('リクエスト中はtrue', () => {
        waitFor(async () => {
          await get()
          expect(isLoading).toBe(true)
        })
      })
      it('リクエスト後はfalse', () => {
        waitFor(async () => await get())
        expect(isLoading).toBe(false)
      })
    })
    describe('update', () => {
      it('リクエスト中はtrue', () => {
        waitFor(async () => {
          await update('post', {})
          expect(isLoading).toBe(true)
        })
      })
      it('リクエスト後はfalse', () => {
        waitFor(async () => await update('post', {}))
        expect(isLoading).toBe(false)
      })
    })
    describe('remove', () => {
      it('リクエスト中はtrue', () => {
        waitFor(async () => {
          await remove()
          expect(isLoading).toBe(true)
        })
      })
      it('リクエスト後はfalse', () => {
        waitFor(async () => await remove())
        expect(isLoading).toBe(false)
      })
    })
  })
})

tara is oktara is ok

useRequest.tsでsetIsLoadingの処理を削除してみてテストしてみる。

loadingの状態は変わらないはずなのにテストが通ってしまっている😓

 PASS  __tests__/hooks/useRequest.test.ts
  useRequest
    isLoading
      ✅ 初期値はfalse (2 ms)
      get
        ✅ リクエスト中はtrue (37 ms)
        ✅ リクエスト後はfalse (1 ms)
      update
        ✅ リクエスト中はtrue (1 ms)
        ✅ リクエスト後はfalse
      remove
        ✅ リクエスト中はtrue (1 ms)
        ✅ リクエスト後はfalse

Test Suites: 1 passed, 1 total
Tests:       7 passed, 7 total
Snapshots:   0 total

そもそもwaitForは先頭にawaitをつけないと全てパスする様になっていた😢

tara is oktara is ok

get methodを呼ぶ前に関数内でtokenを取得しているので、auth0のfunctionに遅延を入れたらフラグが立つのではないか?

import { renderHook, waitFor } from '@testing-library/react'
import MockAdapter from 'axios-mock-adapter'

import { useRequest } from '@/common/hooks/useRequest'

import axios from '../../src/common/libs/axios'

jest.mock('@auth0/auth0-react', () => ({
  useAuth0: () => ({
    getAccessTokenSilently: jest.fn(
      () =>
        new Promise((resolve) =>
          setTimeout(() => resolve('mocked-token'), 1000),
        ),
    ),
  }),
}))

const url = 'https://example.com'

describe('useRequest', () => {
  const mock = new MockAdapter(axios)
  const {
    result: {
      current: { isLoading, get, update, remove },
    },
  } = renderHook(() => useRequest({ url }))
  afterEach(() => mock.reset())
  describe('isLoading', () => {
    test('初期値はfalse', () => expect(isLoading).toBe(false))
    describe('get', () => {
      it('リクエスト中はtrue', () => {
        waitFor(async () => {
          get()
          expect(isLoading).toBe(true)
        })
      })
      it('リクエスト後はfalse', () => {
        get()
        expect(isLoading).toBe(false)
      })
    })
    describe('update', () => {
      it('リクエスト中はtrue', () => {
        waitFor(() => {
          update('post', {})
          expect(isLoading).toBe(true)
        })
      })
      it('リクエスト後はfalse', () => {
        update('post', {})
        expect(isLoading).toBe(false)
      })
    })
    describe('remove', () => {
      it('リクエスト中はtrue', () => {
        waitFor(() => {
          remove()
          expect(isLoading).toBe(true)
        })
      })
      it('リクエスト後はfalse', () => {
        remove()
        expect(isLoading).toBe(false)
      })
    })
  })
})

→ 無理だった

このスクラップは2024/01/29にクローズされました